#python-3.x #closures
Вопрос:
Как захватить значение (в отличие от ссылки) в закрытии python?
Что я пробовал:
Вот функция, которая составляет список функций без параметров, каждая из которых выдает строку:
def makeListOfFuncs( strings):
list = []
for str in strings:
def echo():
return str
list.append( echo)
return list
Если бы замыкания в python работали как на любом другом языке, я бы ожидал, что вызов, подобный этому:
for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
print( animalFunc())
… даст такой результат:
bird
fish
zebra
Но вместо этого в python мы получаем:
zebra
zebra
zebra
По — видимому, происходит то, что закрытие echo
функции захватывает ссылку на str, в отличие от значения во фрейме вызова во время построения закрытия.
Как я могу определить функции Makelistoffunces, чтобы получить «птицу», «рыбу», «зебру»?
Ответ №1:
Закрытие Python не является странным, если вы знаете, как внутреннее закрытие работает в python.
def makeListOfFuncs( strings):
list = []
for str in strings:
def echo():
return str
list.append( echo)
return list
Вы возвращаете список закрытий. переменная «str» является общей для областей, она находится в двух разных областях. Когда python видит это, он создает промежуточный объект. этот объект содержит ссылку на другой объект, который является «str». и для каждого закрытия это одна и та же ячейка. Вы можете это проверить:
closures_list= makeListOfFuncs( ['bird', 'fish', 'zebra'])
# Those will return same cell address
closures_list[0].__closure__
closures_list[1].__closure__
closures_list[2].__closure__
При вызове makeListOfFuncs
он запустит цикл for , и в конце цикла промежуточный объект укажет на «зебру». Поэтому , когда вы вызываете каждое закрытие print( animalFunc())
, оно посещает промежуточный объект, который будет ссылаться на «зебру».
Вы должны подумать о том, что происходит, когда python создает функцию и когда он ее оценивает.
def echo():
return str
Нам нужно каким-то образом зафиксировать значение «str» в создаваемой функции. Потому что если вы подождете, пока функция не будет оценена, то будет слишком поздно. Другим решением было бы определение значения по умолчанию:
def makeListOfFuncs( strings):
list = []
for str in strings:
def echo(y=str):
return y
list.append( echo)
return list
for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
print( animalFunc())
Это решение работает, потому что значения по умолчанию оцениваются во время создания. Поэтому при echo
создании будет использоваться значение по умолчанию «str». значение по умолчанию не будет указывать на ячейку.
на первой итерации значением по умолчанию будет «птица». Python не будет рассматривать это как free variable
. в этом случае мы даже не создаем закрытие, мы создаем функцию. на следующей итерации будет напечатана «рыба», а на последней итерации — «зебра».
Ответ №2:
Я нашел ответ здесь, в блоге Боба Кернера.
def makeListOfFuncs( strings):
list = []
for str in strings:
def generateFunc( str):
def echo():
return str
return echo
list.append( generateFunc( str))
return list
for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
print( animalFunc())
Закрытие Python-это странно.