Поскольку никто не ответил на часть «что такое механизм», и это удивило меня, когда я впервые прочитал его, вот что:
Это:
ls = [lambda: x for x in range(5)]
Это немного похоже на:
ls = []
x = 0
ls.append(lambda: x)
x = 1
ls.append(lambda: x)
x = 2
ls.append(lambda: x)
x = 3
ls.append(lambda: x)
x = 4
ls.append(lambda: x)
Каждая из этих лямбд имеет свою область видимости, но ни одна из этих областей не содержит x
. Таким образом, все они будут читать значение x
, просматривая внешнюю область видимости, поэтому, конечно, все они должны ссылаться на один и тот же объект. К тому времени, когда любой из них вызывается, цикл завершен, и этот объект является целым числом 4
.
Так что, хотя эти лямбды выглядят как функции, включающие только неизменяемые значения, они все равно могут подвергаться побочным эффектам, поскольку зависят от привязок во внешней области видимости, и эта область может измениться.
Конечно, вы могли бы дополнительно изменить то, что возвращают эти лямбды, связав x
, или заставить их выдать ошибку, открепив ее. В версии для понимания списка, x
ограничен только частной областью внутри понимания списка, поэтому вы не можете связываться с ним (так легко).
Решение, конечно, состоит в том, чтобы расположить вещи так, чтобы каждая лямбда имела x
в своей локальной области видимости (или, по крайней мере, некоторую внешнюю область, которая не является общей для лямбд), чтобы они могли ссылаться на разные объекты. Способы сделать это были показаны в других ответах.