closure = decorator?
Terry Reedy
tjreedy at udel.edu
Fri Oct 11 16:51:15 EDT 2013
On 10/11/2013 12:44 PM, Steven D'Aprano wrote:
> On Fri, 11 Oct 2013 15:01:40 +0300, Jussi Piitulainen wrote:
>
>> Steven D'Aprano writes:
>>> Closures have nothing to do with *arguments*. A better definition of a
>>> closure is that it is a function together with a snapshot of the
>>> environment it was called from.
> [...]
>> Second, it's precisely not (a snapshot of) the environment where the
>> function is *called* from, it's (a snapshot of) the environment where
>> the function was *created* in. This is the whole *point*.
>
> Ah yes, of course you are right. I actually knew that, it was a slip of
> the brain that I wrote it wrong :-(
>
> Thanks for the correction.
The closure is also not a 'snapshot' but a reference to (or preservation
of) (relevant parts of) the environment. A snapshot of the environment
at the time of definition would have been much easier to implement.
x = 1
def outer():
y = 1
def inner():
return x + y
y = 2
return inner
x = 2
print(outer()())
# 4
In a sense, all user functions are closures in that they have, and have
always had, a reference to their definition module environment -- the
readonly .__globals__ attribute (probably .func_globals in 2.x).
This lexical, as opposed to dynamic scoping, becomes noticable when one
import a function from another module, as for testing. Because
.__globals__ is read-only, one must monkey-patch the module of
definition to change the function's global (modular) environment, as
when replacing an object it uses with a mock. I ran into this when
testing Idle methods that use a tk message box to display a message and
wait for input (sometimes text, always a mouse click) from a human user.
What is relatively new (and tricky) is capturing local names of
surrounding functions while maintaining both late binding and
independent writability for each closure. This last means that the
following works:
def account():
balance = 0
def trans(amt):
nonlocal balance
balance += amt
return balance
return trans
xmasfund = account()
pettycash = account()
print(xmasfund(100))
# 100
print(pettycash(50))
# 50
print(xmasfund(-100))
# 0
print(pettycash(-25))
# 25
Closures and decorators are *really* two different subjects.
--
Terry Jan Reedy
More information about the Python-list
mailing list