Подтвердить что ты не робот

В 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
4b9b3361

Ответ 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 пытается связать имя с правой стороны, он еще не существует.