在Python中,为什么lambda表达式可以引用正在定义的变量而不是列表?

Sah*_*and 29 python

这比任何事情都更具好奇心,但我注意到以下内容.如果我定义一个自引用的lambda,我可以轻松地做到:

>>> f = lambda: f
>>> f() is f
True
Run Code Online (Sandbox Code Playgroud)

但是,如果我要定义一个自引用列表,我必须在多个语句中执行:

>>> 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
[[...]]
Run Code Online (Sandbox Code Playgroud)

我也注意到这不仅限于列表,但似乎除了lambda之外的任何其他表达式都不能引用赋值左边的变量.例如,如果您有一个带有一个节点的循环链表,那么您不能简单地去:

>>> 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
Run Code Online (Sandbox Code Playgroud)

相反,您必须在两个语句中执行此操作:

>>> n = Node(None)
>>> n.next = n
>>> n is n.next
True
Run Code Online (Sandbox Code Playgroud)

有谁知道这种差异背后的哲学是什么?我知道递归lambda的使用频率更高,因此支持自引用对于lambdas很重要,但为什么不允许它进行任何赋值?

编辑:下面的答案非常清楚地澄清了这一点.原因是每次调用lambda时都会评估Python中lambdas中的变量,而不是在定义它时.在这个意义上,它们与使用定义的函数完全相同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
Run Code Online (Sandbox Code Playgroud)

iCo*_*dez 26

调用函数时,将评估lambda中的表达式,而不是在定义函数时.

换句话说,f在你调用它之前,Python不会评估你的lambda内部.到那时,f已经在当前范围内定义了(它本身就是lambda).因此,没有NameError提出.


请注意,对于这样的行,情况并非如此:

a = [a]  
Run Code Online (Sandbox Code Playgroud)

当Python解释这种类型的行(称为赋值语句)时,它将=立即计算右侧的表达式.此外,NameError将针对当前范围中未定义的右侧使用的任何名称引发a .

  • 它不是运行时与编译时的区别,而是定义时间与呼叫时间的区别.函数定义和调用都在运行时发生. (5认同)

Bre*_*arn 9

因为lambda是一个函数,并且在调用函数之前不会执行函数体.

换句话说,另一种方法是:

def f():
    return f
Run Code Online (Sandbox Code Playgroud)

但你是正确的,你不能在表达式中这样做,因为它def是一个语句,所以它不能在表达式中使用.


Aar*_*all 5

我们可以在反汇编 lambda 函数时看到(这是 Python 2.6 和 3.3 中相同的输出)

>>> import dis
>>> f = lambda: f
>>> dis.dis(f)
  1           0 LOAD_GLOBAL              0 (f)
              3 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

我们证明我们不需要在调用 f 之前加载它,因此它已经被全局定义,因此被存储,所以这是有效的:

>>> f is f()
True
Run Code Online (Sandbox Code Playgroud)

但是当我们这样做时:

>>> a = [a]    
Run Code Online (Sandbox Code Playgroud)

我们有一个错误(如果a之前未定义),并且如果我们反汇编 Python 的 this 实现。

>>> 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    
Run Code Online (Sandbox Code Playgroud)

我们看到a我们在存储之前尝试加载。