这比任何事情都更具好奇心,但我注意到以下内容.如果我定义一个自引用的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 .
因为lambda是一个函数,并且在调用函数之前不会执行函数体.
换句话说,另一种方法是:
def f():
return f
Run Code Online (Sandbox Code Playgroud)
但你是正确的,你不能在表达式中这样做,因为它def是一个语句,所以它不能在表达式中使用.
我们可以在反汇编 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我们在存储之前尝试加载。