2

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).

Community
  • 1
  • 1
jdavid_1385
  • 107
  • 6

3 Answers3

4

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • This is only part of the story: It's the explanation because `i` in the example is global. – Marcin May 30 '12 at 11:15
  • 1
    That's what I said: "Closures are only introduced if there are variables to be referenced outside of the global (module) scope". – Martijn Pieters May 30 '12 at 11:16
  • Yes, but you don't identify any global variables in OP's code; in fact, you don't really engage with OP's code or how this applies to it. Just because your explanation is *correct* it does not follow that it is complete. – Marcin May 30 '12 at 11:17
  • 1
    @Marcin: The OP asked a fairly theoretical question about closures in general, not a specific fix for his code examples. Still, I've updated my answer. – Martijn Pieters May 30 '12 at 11:20
  • It's pretty clear he wants to know why his code doesn't behave as he expects. – Marcin May 30 '12 at 11:21
  • Yes, sure, but in the context of the other question. "I am trying a piece of code from the question in Lexical closures in Python". I think we could agree that we disagree? :-) – Martijn Pieters May 30 '12 at 11:44
  • Martijn is rigth it was purely theorethical. As you have seen the second example in my question does the rigth thing. But still it was practical since in the question referenced, Null303 gave an answer with the result as having a closure. Is clear that people should first check their answers troughoutly before posting them. Thanks both anyway – jdavid_1385 May 30 '12 at 11:57
  • @poissonbreaker If you want to be that pedantic, the only question you have posted is "Shouldn't it be?: [code]", which is a yes/no question. – Marcin May 30 '12 at 12:11
  • @Marcin I think you took me wrong. I have just put it a bit unclear, when I wrote "Is clear that people should first check their answers troughoutly before posting them" I was refering to that answer on [link](http://stackoverflow.com/questions/233673/lexical-closures-in-python). Your answers were awesome. Sorry for the misunderstanding. – jdavid_1385 Oct 14 '12 at 10:33
2

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.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • The language reference could use an explanation *when* `func_closure` is None, in my opinion.. – Martijn Pieters May 30 '12 at 11:18
  • @MartijnPieters I think it's pretty clear that it's `None` whenever there are no bindings to be stored there. However, this also leaves implementers free to provide an empty tuple. The safe way to check `func_closure` is to take its bool value. – Marcin May 30 '12 at 11:19
1

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)
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • In fact, the only way to do this without closure. – Marcin May 30 '12 at 11:13
  • That answer is already in the Linked question. I suggest you to reference it, that way people can have a more complete list for that specific issue. I won't give you points since you didn't answer for what I asked – jdavid_1385 May 30 '12 at 12:01