Ответ 1
Я думаю, что это довольно очевидно, что происходит, когда вы думаете о i
как имя, а не какое-то значение. Ваша лямбда-функция делает что-то вроде "возьмите x: найдите значение i, вычислите я ** x"... поэтому, когда вы действительно запускаете функцию, она смотрит вверх i
именно тогда, поэтому i
есть 4
.
Вы также можете использовать текущий номер, но вы должны заставить Python привязать его к другому имени:
def makeActions():
def make_lambda( j ):
return lambda x: j * x # the j here is still a name, but now it wont change anymore
acts = []
for i in range(5):
# now you're pushing the current i as a value to another scope and
# bind it there, under a new name
acts.append(make_lambda(i))
return acts
Это может показаться запутанным, потому что вам часто причисляют, что переменная и ее значение - одно и то же, что верно, но только на языках, которые фактически используют переменные. У Python нет переменных, но вместо имен.
О вашем комментарии, на самом деле я могу проиллюстрировать точку немного лучше:
i = 5
myList = [i, i, i]
i = 6
print(myList) # myList is still [5, 5, 5].
Вы сказали, что изменили я на 6, это не то, что на самом деле произошло: i=6
означает "у меня есть значение, 6
и я хочу назвать его i
". Тот факт, что вы уже использовали i
как имя, не имеет ничего общего с Python, он просто переназначает имя, а не меняет его значение (которое работает только с переменными).
Можно сказать, что в myList = [i, i, i]
любое значение i
в настоящее время указывает (число 5) получает три новых имени: mylist[0], mylist[1], mylist[2]
. Это то же самое, что происходит при вызове функции: аргументам присваиваются новые имена. Но это, вероятно, противоречит любой интуиции о списках...
Это может объяснить поведение в примере: вы назначаете mylist[0]=5
, mylist[1]=5
, mylist[2]=5
- неудивительно, что они не меняются при переназначении i
. Если i
было чем-то немым, например, списком, то изменение i
отразилось бы на всех записях в myList
тоже, потому что у вас просто разные имена для одного и того же значения!
Простой факт, что вы можете использовать mylist[0]
в левой части =
, доказывает, что это действительно имя. Мне нравится называть =
оператор присваивания имени: он берет имя слева и выражение справа, затем вычисляет выражение (функцию вызова, ищет значения позади имен), пока оно не имеет значение и, наконец, дает имя к значению. Он не меняет ничего.
Для комментариев Marks о компиляции функций:
Ну, ссылки (и указатели) имеют смысл только тогда, когда у нас есть какая-то адресная память. Значения хранятся где-то в памяти, и ссылки ведут вас туда. Использование ссылки означает переход в это место в памяти и что-то с ней делать. Проблема в том, что ни одна из этих концепций не используется Python!
У Python VM нет понятия памяти - значения плавают где-то в пространстве, а имена - это небольшие теги, связанные с ними (красной строкой). Имена и ценности существуют в разных мирах!
Это имеет большое значение при компиляции функции. Если у вас есть ссылки, вы знаете местоположение памяти объекта, на который вы ссылаетесь. Затем вы можете просто заменить ссылку на это место. Имена, с другой стороны, не имеют места, поэтому то, что вам нужно сделать (во время выполнения), следует за маленькой красной строкой и использовать все, что находится на другом конце. Так компилируются функции Python: Где когда в коде есть имя, оно добавляет инструкцию, которая будет определять, что означает это имя.
Таким образом, в основном Python полностью выполняет компиляцию функций, но имена компилируются как поисковые запросы в пространствах имен вложенности, а не как некоторая ссылка на память.
Когда вы используете имя, компилятор Python попытается выяснить, к какому пространству имён принадлежит. Это приводит к инструкции для загрузки этого имени из найденного им пространства имен.
Что возвращает вас к исходной проблеме: в lambda x:x**i
i
скомпилируется как поиск в пространстве имен makeActions
(потому что там был использован i
). Python понятия не имеет и не заботится о ценности, стоящей за ней (это даже не должно быть допустимым именем). Тот, который запускает код i
, просматривает в нем исходное пространство имен и дает более или менее ожидаемое значение.