I am trying a piece of code from the question in Lexical closures in Python
flist = []
for i in xrange(3):
def func(x): return x*i
flist.append(func)
for f in flist:
print f.func_closure
The output is:
None
None
None
Shouldn't it be?:
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
I have got the above output using the following code:
flist = []
def actualFact():
for i in xrange(3):
def func(x): return x * i
flist.append(func)
for f in flist:
print f.func_closure
I am using Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39).
Closures are only introduced if there are variables to be referenced outside of the global (module) scope:
>>> def foo():
... def bar(): pass
... return bar
...
>>> foo().func_closure is None
True
>>> spam = 'eggs'
>>> def foo():
... def bar(): return spam
... return bar
...
>>> foo().func_closure is None
True
Only when the inner function refers to a variable in the surrounding scope are closures generated:
>>> def foo():
... spam = 'eggs'
... def bar(): return spam
... return bar
...
>>> foo().func_closure is None
False
>>> foo().func_closure
(<cell at 0x108472718: str object at 0x108471de0>,)
Note that you actually have to refer to a variable in the surrounding scope. Simply ignoring the scope gives you None
again:
>>> def foo():
... spam = 'eggs'
... def bar(): pass
... return bar
...
>>> foo().func_closure is None
True
In your first example, i
is a module-scope variable, only in your second example do you introduce a new scope by wrapping the code in a new function actualFact
.
The language reference specifies that func_closure
is "None or a tuple of cells that contain bindings for the function’s free variables."
Now, note the difference between your two versions: in the first version i
is a module-level (i.e. global) variable. The result of evaluating each of the functions is the same:
>>> [f(2) for f in flist]
[4, 4, 4]
In each function, i
is not free, but refers to the global i
, so no, the output should not be a list of non-zero-length tuples.
In practice, you probably don't care about the value of func_closure
, unless you're doing some fairly deep magic. If you are doing something magic, note that given the specification, there seems to be no good reason why func_closure
should not be an empty tuple if there are no free variables, so handle that case appropriately if you want your code to be portable between even different point-versions of python.
A cheap way to do this without a closure
for i in xrange(3):
def func(x, i=i): return x*i
flist.append(func)