Запутаться с лямбда и понимание списка - PullRequest
14 голосов
/ 09 февраля 2012

Прочитать вопрос о переполнении стека когда-нибудь со следующим синтаксисом

In [1]: [lambda: x for x in range(5)][0]()
Out[1]: 4
In [2]: [lambda: x for x in range(5)][2]()
Out[2]: 4

Но мне трудно понять, почему именно результат получается как 4, Насколько я понимаю, это всегда дает последнее значение списка в качестве вывода,

In [4]: [lambda: x for x in [1,5,7,3]][0]()
Out[4]: 3

но все еще не уверен, как этот синтаксис заканчивается последним значением.

Был бы очень рад, если бы я мог получить правильное объяснение этого синтаксиса

Ответы [ 5 ]

15 голосов
/ 09 февраля 2012

Это на самом деле не относится ни к списку, ни к лямбдам. Это о правилах обзора в Python. Давайте перепишем понимание списка в эквивалентный цикл:

funcs = []
for x in range(5):
    def f(): return x
    funcs.append(f)
funcs[0]() # returns 4

Здесь мы видим, что мы последовательно строим функции и сохраняем их в списке. Когда вызывается одна из этих функций, все, что происходит, - это значение x, которое ищется и возвращается. Но x - это переменная, значение которой изменяется, поэтому окончательное значение 4 - это то, что всегда возвращается. Вы даже можете изменить значение x после цикла, например,

x = 32 
funcs[2]() # returns 32

Чтобы получить ожидаемое поведение, Python должен охватить содержимое for как блок; это не так. Обычно это не проблема, и ее достаточно легко обойти.

4 голосов
/ 09 февраля 2012

Для LC с n итерациями x присваиваются элементы от 0 до n-1 исходной последовательности.На последней итерации x назначается последний элемент.Следует отметить, что это всегда одно и то же x, и вызов функции в конце возвращает то, что x удерживалось последним.

3 голосов
/ 09 февраля 2012

Позвольте мне взломать код для моего понимания

In [39]: [lambda: x for x in [1,5,7,3]]
Out[39]: 
[<function <lambda> at 0x2cd1320>,
 <function <lambda> at 0x2cd12a8>,
 <function <lambda> at 0x2cd10c8>,
 <function <lambda> at 0x2cd1050>]

выше дает список функций

In [40]: [lambda: x for x in [1,5,7,3]][1]
Out[40]: <function <lambda> at 0x2cd1488>

Индекс 1 дает 1 функцию из списка функций.

Теперь эта функция будет применяться к x, который всегда имеет последнее значение в списке. То, что у всегда дает последнее значение в качестве результата.

как в коде ниже.

In [41]: [lambda: 2][0]()
Out[41]: 2


In [42]: alist = [1,5,7,3,4,5,6,7]

x для x в [1,5,7,3] эквивалентно приведенной ниже функции f (x). и
лямбда: х для х в [1,5,7,3] эквивалентно лямбда: 3

In [43]: def f(x):
   ....:     for x in alist:
   ....:         pass
   ....:     return x
In [44]: print f(alist)
7
1 голос
/ 11 февраля 2012

Как уже говорили другие, вы столкнулись с общей проблемой замыкания цикла;т.е. проблема в том, что замыкание захватывает переменные по ссылке.В этом случае вы захватываете переменную, значение которой меняется со временем (как и для всех переменных цикла), поэтому при запуске ее значение отличается от значения, которое вы создали.

Чтобы выполнить то, что вы хотите, выНеобходимо зафиксировать значение переменной во время создания лямбды.sblom предложил решение обернуть его в немедленно выполняемую функцию:

[(lambda(i): lambda: i)(x) for x in range(5)][2]()

Другое решение, которое вы часто увидите в Python, использует аргументы по умолчанию, которые также оценивают значение во время создания:

[lambda i=x: i for x in range(5)][2]()

(обычно пишется lambda x=x: x, но я переименовал переменную для ясности)

1 голос
/ 09 февраля 2012

Это исправит:

[(lambda(i): lambda: i)(x) for x in range(5)][2]()

Проблема в том, что вы не захватываете значение x на каждой итерации понимания списка, вы захватываете переменную каждый раз до конца.

...