Re: Python Mystery Theatre -- Episode 2: Así Fue

Bengt Richter bokr at oz.net
Wed Jul 16 10:36:19 EDT 2003


On Tue, 15 Jul 2003 10:54:15 +0200, "Helmut Jarausch" <jarausch at igpm.rwth-aachen.de> wrote:

>Fredrik Lundh wrote:
>> Helmut Jarausch wrote:
>> 
>> 
>>>OK, I believe to know why the last line
>>>print '3' three times, since only a reference
>>>to 'f' is stored within the lambda expression
>>>and this has the value 'thrice' when 'print'
>>>is executed.
>>>
>>>But how can I achieve something like an
>>>evaluation of one indirection so that
>>>a reference to the function referenced by 'f'
>>>is stored instead.
>> 
>> 
>> assuming you meant "the function reference by 'f' when the lambda
>> is created", the easiest solution is to use default argument binding:
>> 
>>     flam = [lambda x,f=f: f(x) for f in funcs]
>> 
>> the "f=f" construct will bind the inner name "f" to the current value of
>> the outer "f" for each lambda.
>> 
>> the nested scopes mechanism is often introduced as the "right way" to
>> do what was done with argument binding in earlier versions of Python.
>> however, nested scopes bind *names*, while argument binding binds
>> *values*.
>
>Many thanks for that hint,
>still a part of the question remains (unclear to me)
>
>Obviously Python allows references to references, since
>e.g. 'once' (the 'name' of a function) is a reference to
>the code and 'f' is a reference to that reference. (you call it
>name binding)
>A similar situation arises in Maple and there one has the choice
>to either derefence all references down to the real object
>or to just derefence a single time.
>
>Example
>
>def once(x): return x
>def twice(x): return 2*x
>ref= once
>def caller():
>     callee=ref   # (*)
>     print callee(1)
>
>caller()  # prints 1
>ref= twice
>caller()  # prints 2  so that demonstrates name binding
>
>how can I get the current value (like 'xdef' in TeX)
>of 'ref' in the assignment (*) above, so that
>'callee' becomes an (immutable) reference to 'once' ?
>
IWT the straight-forward way would be to capture ref in a callable class instance:

 >>> def once(x): return x
 ...
 >>> def twice(x): return 2*x
 ...
 >>> class Caller(object):
 ...     def __init__(self): self.callee = ref # global ref at time of init
 ...     def __call__(self): print self.callee(1)
 ...
 >>> ref = once
 >>> caller = Caller()
 >>> caller()
 1
 >>> ref = twice
 >>> caller()
 1
 >>> caller2 = Caller()
 >>> caller2()
 2
 >>> caller()
 1

You could also make a factory function that captures ref using a closure:

 >>> def ffun():
 ...     callee = ref
 ...     def caller(): print callee(1)
 ...     return caller
 ...
 >>> ref = once
 >>> caller = ffun()
 >>> caller()
 1
 >>> ref = twice
 >>> caller_twice = ffun()
 >>> caller_twice()
 2

If you just wanted the first-used ref to be "sticky," you could do something kludgy:
(note that this pospones capturing ref until the first call vs class instance creation or ffun call)

 >>> def caller():
 ...     if not hasattr(caller,'callee'): setattr(caller,'callee',ref)
 ...     print caller.callee(1)
 ...
 >>> ref = once
 >>> caller()
 1
 >>> ref = twice
 >>> caller()
 1

You can "unstick" it and have it stick again:

 >>> del caller.callee
 >>> caller()
 2
 >>> ref = once
 >>> caller()
 2

Regards,
Bengt Richter




More information about the Python-list mailing list