Подтвердить что ты не робот

Поведение weird закрытия в python

У меня есть следующий простой код:

def get():
    return [lambda: i for i in [1, 2, 3]]

for f in get():
    print(f())

Как и ожидалось из моего знания python, вывод будет 3 - весь список будет содержать последнее значение i. Но как это работает внутри страны?

AFAIK, переменные python просто ссылаются на объекты, поэтому первое закрытие должно заключать объект в первую очередь i reference - и этот объект определенно 1, а не 3 O_O. Как происходит, что закрытие python включает в себя переменную, а не объект, эту ссылку на переменную? Сохраняет ли это имя переменной как обычный текст, некоторую "ссылку на переменную" или что?

4b9b3361

Ответ 1

Закрытия не относятся к переменным, а скорее к областям. Поскольку последнее значение i в своей области составляет "3", все три закрытия возвращаются одинаково. Чтобы "заблокировать" текущее значение переменной, создайте для него только новую область:

def get() : return [ (lambda x: lambda: x)(i) for i in [ 1, 2, 3 ] ]
for f in get() : print( f() )

Ответ 2

Как указывает @thg435, лямбда не будет инкапсулировать значения в этот момент, а скорее область. Слишком малые способы решения этой проблемы:

аргумент lambda default "hack"

[ lambda v=i: v for i in [ 1, 2, 3 ] ]

Или используйте functools.partial

from functools import partial
[ partial(lambda v: v, i) for i in [ 1, 2, 3 ] ]

По существу, вам нужно переместить область, чтобы она была локальной для создаваемой функции. Обычно мне нравится использовать partial чаще, так как вы можете передать его вызываемым, а любые args и kargs для создания вызываемого с правильным закрытием. Внутренне это обертывание вашего оригинального вызываемого, чтобы область была смещена для вас.

Ответ 3

Каждый lambda фактически ссылается на тот же i, который является переменной, созданной пониманием списка. По завершении понимания списка i поддерживает значение конечного элемента, которому он был назначен, до тех пор, пока он не выйдет из области действия (что предотвращается путем инкапсуляции его внутри функции и его возврата, а именно lambda). Как указывали другие, закрытие не поддерживает копии значений, а скорее поддерживает ссылки на переменные, которые были определены в пределах их объема.