Вложенная функция ищет переменные из родительской области при выполнении, а не когда определено.
Тело функции скомпилировано, а «свободные» переменные (не определенные в самой функции путем назначения) проверяются, а затем привязываются как замыкающие ячейки к функции, причем код использует индекс для ссылки на каждую ячейку. pet_function
, таким образом, имеет одну свободную переменную (cage
), которая затем ссылается через ячейку замыкания, индекс 0. Сама замыкание указывает на локальную переменную cage
в функции get_petters
.
Когда вы на самом деле вызываете функцию, это закрытие затем используется для просмотра значения cage
в окружающем пространстве во время вызова функции . Здесь кроется проблема. К тому моменту, когда вы вызываете свои функции, функция get_petters
уже выполняется, вычисляя ее результаты. Локальная переменная cage
в какой-то момент во время этого выполнения была назначена каждой из строк 'cow'
, 'dog'
и 'cat'
, но в конце функции cage
содержит это последнее значение 'cat'
. Таким образом, когда вы вызываете каждую из динамически возвращаемых функций, вы получаете значение 'cat'
.
Обход - это не полагаться на замыкания. Вместо этого вы можете использовать частичную функцию , создать новую область функций или привязать переменную как значение по умолчанию для параметра ключевого слова .
functools.partial()
: from functools import partial
def pet_function(cage=None):
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
def scoped_cage(cage=None):
def pet_function():
print "Mary pets the " + cage.animal + "."
return pet_function
yield (animal, partial(gotimes, scoped_cage(cage)))
def pet_function(cage=cage):
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, pet_function))
Нет необходимости определять функцию scoped_cage
в цикле, компиляция выполняется только один раз, а не на каждом итерация цикла.