В 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 [[...]] 

Я также заметил, что это не ограничивается списками, но похоже, что любое другое выражение, отличное от лямбда, не может ссылаться на переменную слева от назначения. Например, если у вас есть циклический связанный список с одним узлом, вы не можете просто пойти:

 >>> 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 

Кто-нибудь знает, что такое философия, лежащая в основе этой разницы? Я понимаю, что рекурсивная лямбда используется гораздо чаще, и, следовательно, поддержка самореференции важна для лямбда, но почему бы не разрешить ее для каких-либо заданий?

EDIT: ответы ниже объясняют это довольно хорошо. Причина в том, что переменные в 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 

Выражение внутри лямбда оценивается при вызове функции, а не тогда, когда она определена.

Другими словами, Python не будет оценивать f внутри вашей лямбды, пока вы ее не назовете. И к тому времени f уже определен в текущей области (это сама лямбда). Следовательно, No NameError не NameError .


Обратите внимание, что это не относится к такой строке:

 a = [a] 

Когда Python интерпретирует этот тип строки (известный как оператор присваивания), он будет оценивать выражение справа от = немедленно. Более того, NameError будет поднят для любого имени, используемого справа, которое не определено в текущей области.

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

Другими словами, другой способ сделать это:

 def f(): return f 

Но вы правы, что не можете сделать это в выражении, потому что def – это оператор, поэтому он не может использоваться в выражении.

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