Лямбды из списка понимания возвращают лямбда при вызове

Я пытаюсь перебрать лямбда-функцию по списку, как вtest.pyи я хочу получить результат вызова лямбды, а не сам объект функции. Однако следующий вывод действительно смутил меня.

------test.py---------
#!/bin/env python
#coding: utf-8

a = [lambda: i for i in range(5)]
for i in a:
    print i()

--------output---------
<function <lambda> at 0x7f489e542e60>
<function <lambda> at 0x7f489e542ed8>
<function <lambda> at 0x7f489e542f50>
<function <lambda> at 0x7f489e54a050>
<function <lambda> at 0x7f489e54a0c8>

Я изменил имя переменной при выводе результата вызоваt как следует, и все идет хорошо. Мне интересно, что все это значит. ?

--------test.py(update)--------
a = [lambda: i for i in range(5)]
for t in a:
    print t()

-----------output-------------
4
4
4
4
4
 juanpa.arrivillaga14 июл. 2016 г., 11:28
Ой, извините за пометку. Я также неправильно прочитал!
 Wayne Conrad14 июл. 2016 г., 19:14
Приведенная выше демонстрация вежливости и смирения - это то, что мне нравится в сообществе Python, особенно тех, кто часто посещает SO.
 Daniel Roseman14 июл. 2016 г., 11:03
Извините за закрытие, я неправильно понял вопрос.
 jpmc2614 июл. 2016 г., 18:02
Я изменил название, потому что это былодействительно легко пропустить свой вопрос и подумать, что у вас была простая проблема позднего связывания вместо проблемы, связанной с повторным использованием имени переменной. (Об этом свидетельствуют ошибочные замыкания и ответы.) Новый заголовок привлекает внимание к реальной проблеме, которая вас волнует. Если вы хотите перефразировать это из того, что я сделал, пожалуйста, убедитесь, что вы сохранили этот аспект.

Ответы на вопрос(3)

позднее связываниеЭто означает, что каждая лямбда-функция в списке будет оценивать только переменнуюi когда вызывается, ине когда определено. Вот почему все функции возвращают одно и то же значение, то есть последнее значениеì (что 4).

Чтобы избежать этого, одним из методов является привязка значенияi в локальный именованный параметр:

>>> a = [lambda i=i: i for i in range(5)]
>>> for t in a:
...   print t()
... 
0
1
2
3
4

Другой вариант заключается в созданиичастичная функция и привязать текущее значениеi как его параметр:

>>> from functools import partial
>>> a = [partial(lambda x: x, i) for i in range(5)]
>>> for t in a:
...   print t()
... 
0
1
2
3
4

Редактировать: Извините, изначально неправильно прочитал вопрос, так как подобные вопросы часто касаются позднего связывания (спасибо@скоро за комментарий).

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

 awesoon14 июл. 2016 г., 11:14
Хорошо, но зачем менять имя переменной вfor цикл меняет вывод?
 awesoon14 июл. 2016 г., 11:26
Моя интерпретация вопроса была «почемуfor i in a печатает лямбды, ноfor k in a печатает числа? ". Конечно, это тоже из-за позднего связывания, но это может быть непонятно для OP. +1 в любом случае
 plamut14 июл. 2016 г., 11:32
Ах, я вижу, возможно, я слишком поспешно объяснил объяснение поведению покойного. :) Это из-за утечки переменной в Python 2, как уже объяснили другие.
 plamut14 июл. 2016 г., 11:16
@soon На какую переменную вы ссылаетесь,t? Это ни на что не влияет, это просто временное имя, которое мы даем каждой функции при переборе списка. Или я неправильно понял вопрос?
Решение Вопроса

сть видимости:

>>> [i for i in xrange(3)]
[0, 1, 2]
>>> i
2

Обратите внимание, что поведение в Python 3 отличается:

>>> [i for i in range(3)]
[0, 1, 2]
>>> i
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'i' is not defined

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

>>> a = [lambda: i for i in range(5)]
>>> a[0]()
4
>>> i = 'foobar'
>>> a[0]()
'foobar'

Поскольку значениеi внутри цикла находится сама лямбда, которую вы получите как возвращаемое значение:

>>> i = a[0]
>>> i()
<function <lambda> at 0x01D689F0>
>>> i()()()()
<function <lambda> at 0x01D689F0>

ОБНОВИТЬПример на Python 2.7:

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [lambda: i for i in range(5)]
>>> for i in a:
...     print i()
... 
<function <lambda> at 0x7f1eae7f15f0>
<function <lambda> at 0x7f1eae7f1668>
<function <lambda> at 0x7f1eae7f16e0>
<function <lambda> at 0x7f1eae7f1758>
<function <lambda> at 0x7f1eae7f17d0>

То же самое на Python 3.4:

Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [lambda: i for i in range(5)]
>>> for i in a:
...     print(i())
... 
4
4
4
4
4

Для получения подробной информации об изменении, касающемся области видимости переменной со списком, смотрите Guido'sпост от 2010.

Мы также сделали еще одно изменение в Python 3, чтобы улучшить эквивалентность между списками и выражениями-генераторами. В Python 2 понимание списка «просачивается» в переменную цикла управления:

x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'

Однако в Python 3 мы решили исправить «грязный маленький секрет» понимания списков, используя ту же стратегию реализации, что и для выражений генератора. Таким образом, в Python 3 вышеприведенный пример (после модификации для использования print (x) :-) напечатает «before», доказывая, что «x» в понимании списка временно затеняет, но не перекрывает «x» в окружении объем.

 keyuan756914 июл. 2016 г., 14:19
@niemmi, хорошо, я понял, спасибо
 Krish Munot23 июл. 2016 г., 19:43
Спасибо за чудесное объяснение! :)
 niemmi14 июл. 2016 г., 11:52
@ keyuan7569 Я тоже рад помочь и научиться чему-то сам :)
 keyuan756914 июл. 2016 г., 11:43
Большое спасибо за ваше подробное объяснение, и я получил это! ;)
 juanpa.arrivillaga14 июл. 2016 г., 11:36
Если подумать, этоне удивительное поведение на всех.
 juanpa.arrivillaga14 июл. 2016 г., 11:23
Утечка объема списочных представлений в Python 2 не влияет на поведение в OP.
 juanpa.arrivillaga14 июл. 2016 г., 11:29
Упс! Я совершенно неправильно понял этот вопрос! Ты совершенно прав! Это удивительное поведение для меня ...
 niemmi14 июл. 2016 г., 11:25
@ juanpa.arrivillaga Вы пытались запустить соответствующий код для обеих версий? По крайней мере, я получаю разные результаты с Python 2.7 и 3.4.
 keyuan756914 июл. 2016 г., 12:04
@niemmi, меня немного смущает, когда я изменил код для for в a: print i () () () () (), он по-прежнему выводит сам объект функции. Является ли это неограниченным рекурсивным, и разумно ли такое поведение?
 niemmi14 июл. 2016 г., 13:21
@ keyuan7569 Ну, лямбда возвращает себя, которая, в свою очередь, возвращает себя и так далее, так что у вас бесконечная рекурсия. Разумно это или нет, это вопрос мнения.
 Tavian Barnes14 июл. 2016 г., 20:47
@ juanpa.arrivillaga "Если подумать, это совсем не удивительно." Если ваша первая мысль была другой, это своего рода определение удивительного поведения :)

lambda: i является анонимной функцией без аргументов, которая возвращает i. Таким образом, вы генерируете список анонимных функций, которые вы можете позже (во втором примере) связать с именемt и вызвать с(), Обратите внимание, что вы можете сделать то же самое с неанонимными функциями:

...   return 42
... 
>>> name = f
>>> name
<function f at 0x7fed4d70fb90>
>>> name()
42

@plamut только что ответил на подразумеваемую другую часть вопроса, поэтому я не буду.

Ваш ответ на вопрос