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

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

Я пытаюсь перебрать lambda func над списком, как в 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
4b9b3361

Ответ 1

В интерпретации списка Python 2 "утечка" переменных в внешнюю область:

>>> [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

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

>>> 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 blogpost с 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" в окружающем сфера.

Ответ 2

Закрытие в Python late-binding, что означает, что каждая функция лямбда в списке будет оценивать только переменную 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

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

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

Ответ 3

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

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

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