Закрытие питона странное поведение
Я пытаюсь кусок кода из вопроса в Lexical замыкания в Python
flist = []
for i in xrange(3):
def func(x): return x*i
flist.append(func)
for f in flist:
print f.func_closure
Выход:
None
None
None
Не должно ли быть?
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
Я получил вышеуказанный вывод, используя следующий код:
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
Я использую Python 2.6.6 (r266:84292, 15 сентября 2010 г., 15:52:39).
3 ответа
Замыкания вводятся только в том случае, если есть переменные, на которые нужно ссылаться за пределами глобальной области (модуля):
>>> 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
Только когда внутренняя функция ссылается на переменную в окружающей области видимости, создаются замыкания:
>>> 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>,)
Обратите внимание, что вы на самом деле должны ссылаться на переменную в окружающей области видимости. Простое игнорирование объема дает вам None
снова:
>>> def foo():
... spam = 'eggs'
... def bar(): pass
... return bar
...
>>> foo().func_closure is None
True
В вашем первом примере i
переменная области видимости модуля, только во втором примере ты вводишь новую область видимости, оборачивая код в новую функцию actualFact
,
Ссылка на язык указывает, что func_closure
"Нет или кортеж ячеек, которые содержат привязки для свободных переменных функции".
Теперь обратите внимание на разницу между вашими двумя версиями: в первой версии i
является переменной уровня модуля (т.е. глобальной) Результат оценки каждой из функций одинаков:
>>> [f(2) for f in flist]
[4, 4, 4]
В каждой функции i
не является бесплатным, но относится к глобальному i
поэтому нет, вывод не должен быть списком кортежей ненулевой длины.
На практике вы, вероятно, не заботитесь о ценности func_closure
, если вы не делаете довольно глубокую магию. Если вы делаете что-то волшебное, обратите внимание, что, учитывая спецификацию, нет веских причин, почему func_closure
не должно быть пустым кортежем, если нет свободных переменных, поэтому обрабатывайте этот случай соответствующим образом, если вы хотите, чтобы ваш код был переносимым даже между разными точечными версиями python.
Дешевый способ сделать это без закрытия
for i in xrange(3):
def func(x, i=i): return x*i
flist.append(func)