Путаница с пониманием лямбды и списка

Снова прочитайте вопрос о переполнении стека со следующим синтаксисом

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

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

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

Ответы

Ответ 1

На самом деле речь идет не о каких-либо перечнях списков или лямбда. Это о правилах определения области в 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 как блоком; это не так. Обычно это не проблема, и ее достаточно легко обойти.

Ответ 2

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

Ответ 3

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

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, которая всегда имеет последнее значение списка. То, что y всегда дает последнее значение в качестве результата.

как в приведенном ниже коде.

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). и
lambda: x для x в [1,5,7,3] эквивалентно лямбда: 3

In [43]: def f(x):
   ....:     for x in alist:
   ....:         pass
   ....:     return x
In [44]: print f(alist)
7

Ответ 4

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

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

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

Ответ 5

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

Чтобы выполнить то, что вы хотите, вам нужно зафиксировать значение переменной во время создания лямбда. 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, но я просто переименовал переменную)