В Python, почему выражение лямбда ссылается на определяемую переменную, но не на список?
Это больше любопытство, чем что-либо, но я только заметил следующее. Если я определяю самореферентную лямбду, я могу сделать это легко:
>>> f = lambda: f
>>> f() is f
True
Но если я определяю самореференционный список, я должен сделать это более чем в одном утверждении:
>>> a = [a]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> a = []
>>> a.append(a)
>>> a[0] is a
True
>>> a
[[...]]
Я также заметил, что это не ограничивается списками, но похоже, что любое другое выражение, отличное от лямбда, не может ссылаться на переменную слева от назначения. Например, если у вас есть циклический связанный список с одним node, вы не можете просто пойти:
>>> class Node(object):
... def __init__(self, next_node):
... self.next = next_node
...
>>> n = Node(n)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'n' is not defined
Вместо этого вы должны сделать это в двух утверждениях:
>>> n = Node(None)
>>> n.next = n
>>> n is n.next
True
Кто-нибудь знает, что такое философия, лежащая в основе этой разницы? Я понимаю, что рекурсивная лямбда используется гораздо чаще, и, следовательно, поддержка самореференции важна для лямбда, но почему бы не разрешить ее для какого-либо назначения?
РЕДАКТИРОВАТЬ: ответы ниже разъясняют это довольно хорошо. Причина в том, что переменные в lambdas на Python оцениваются каждый раз, когда вызывается лямбда, а не когда она определена. В этом смысле они точно такие же, как функции, определенные с помощью def
. Я написал следующий бит кода, чтобы поэкспериментировать с тем, как это работает, как с функциями lambdas, так и с def
, если это может помочь прояснить его для всех.
>>> f = lambda: f
>>> f() is f
True
>>> g = f
>>> f = "something else"
>>> g()
'something else'
>>> f = "hello"
>>> g()
'hello'
>>> f = g
>>> g() is f
True
>>> def f():
... print(f)
...
>>> f()
<function f at 0x10d125560>
>>> g = f
>>> g()
<function f at 0x10d125560>
>>> f = "test"
>>> g()
test
>>> f = "something else"
>>> g()
something else
Ответы
Ответ 1
Выражение внутри лямбда оценивается при вызове функции, а не когда оно определено.
Другими словами, Python не будет оценивать f
внутри вашей лямбды, пока вы ее не назовете. И к тому времени f
уже определен в текущей области (это сама лямбда). Следовательно, нет NameError
.
Обратите внимание, что это не так для строки:
a = [a]
Когда Python интерпретирует этот тип строки (известный как оператор присваивания), он немедленно оценивает выражение справа от =
. Более того, a NameError
будет поднят для любого имени, используемого справа, которое undefined в текущей области.
Ответ 2
Поскольку лямбда является функцией, а тело функции не выполняется до тех пор, пока функция не будет вызвана.
Другими словами, другой способ сделать это:
def f():
return f
Но вы правы, что не можете сделать это в выражении, потому что def
- это оператор, поэтому он не может использоваться в выражении.
Ответ 3
Мы можем видеть, когда мы разбираем лямбда-функцию (это идентичный вывод в Python 2.6 и 3.3)
>>> import dis
>>> f = lambda: f
>>> dis.dis(f)
1 0 LOAD_GLOBAL 0 (f)
3 RETURN_VALUE
Мы демонстрируем, что нам не нужно загружать f до его вызова, после чего он уже определен глобально и, следовательно, сохраняется, поэтому это работает:
>>> f is f()
True
Но когда мы делаем:
>>> a = [a]
У нас есть ошибка (если a
ранее undefined), и если мы разобраем реализацию этого Python.
>>> def foo():
... a = [a]
...
>>> dis.dis(foo)
2 0 LOAD_FAST 0 (a)
3 BUILD_LIST 1
6 STORE_FAST 0 (a)
9 LOAD_CONST 0 (None)
12 RETURN_VALUE
мы видим, что мы пытаемся загрузить a
, прежде чем мы его сохранили.
Ответ 4
Там нет специального корпуса, чтобы это произошло; это просто, как это работает.
На самом деле выражение лямбда не отличается от нормальной. Смысл, я могу это сделать:
x = 1
def f():
print x + 2
f()
3
x = 2
f()
4
Как вы можете видеть, внутри функции значение x
не имеет предопределенного значения - оно выглядело, когда мы действительно запускаем f
. Это включает в себя значение самой функции: мы не смотрим, что представляет f
, пока мы на самом деле не запустим ее, и к тому времени она существует.
Выполнение этого как лямбда не работает по-другому:
del x
f = lambda: x+2
f()
NameError: global name 'x' is not defined
x = 2
f()
4
работает аналогичным образом. В этом случае я пошел дальше и удалил x
, поэтому он больше не был в области, когда был определен f
, а запуск f
в этом случае правильно показывает, что x
не существует. Но после определения x
, то f
снова работает.
В случае с списком это отличается, потому что мы фактически создаем объект прямо сейчас, и теперь все, что находится справа, должно быть связано, прямо сейчас. Как работает python (как я понимаю, и, по крайней мере, на практике это было полезно), нужно учитывать, что все с правой стороны откладывается и привязывается, а затем обрабатывается, и только после этого все полные значения на левая сторона связана и назначена.
Поскольку одно и то же значение находится справа и слева, когда python пытается связать имя с правой стороны, он еще не существует.