概述
Python并不像C或Java这样的静态类型语言一样直接使用变量,而是使用名称并将对象实例与它们标记
在您的示例中,closure
只是一个带有该名称的函数实例
在这里真正发挥作用的是nonlocal
,它导致LOAD_CLOSURE
和BUILD_TUPLE
被如何时检查非本地变量的存在?所述使用并进一步在Python中如何定义自由变量?中引用x
,而不是内部函数字面上命名为closure
。
3 4 LOAD_CLOSURE 0 (x)
关于 nonlocal
对于您的情况,nonlocal
断言在编译时,x
存在于外部作用域(全局变量除外),但实际上它是多余的,因为它在其他地方没有被使用。 文档
最初我写道这是由于重新声明而多余的,但这是不正确的 - nonlocal
防止 重复使用名称,但 x
只是没有在其他任何地方显示,因此效果并不明显
我添加了第三个示例,其中包含一个非常丑陋的生成器,以说明其效果。
使用全局变量的示例(请注意,SyntaxError
在编译时而不是运行时发生)
>>> x = 3
>>> def closure_test():
... def closure():
... nonlocal x
... print(x)
... return closure
...
File "<stdin>", line 3
SyntaxError: no binding for nonlocal 'x' found
>>> def closure_test():
... def closure():
... print(x)
... return closure
...
>>> closure_test()()
3
SyntaxError
与无效本地变量使用相关的示例
>>> def closure_test():
... def closure():
... nonlocal x
... x = 2
... print(x)
... return closure
...
File "<stdin>", line 3
SyntaxError: no binding for nonlocal 'x' found
>>> def closure_test():
... x = 1
... def closure():
... x = 2
... nonlocal x
... print(x)
... return closure
...
File "<stdin>", line 5
SyntaxError: name 'x' is assigned to before nonlocal declaration
使用nonlocal
来设置外部变量的示例
(请注意,这种方法表现不佳,因为将yield
与try:finally
包装起来以更正常的方式显示在closure
实际调用之前)
>>> def closure_test():
... x = 1
... print(f"x outer A: {x}")
... def closure():
... nonlocal x
... x = 2
... print(f"x inner: {x}")
... yield closure
... print(f"x outer B: {x}")
...
>>> list(x() for x in closure_test())
x outer A: 1
x inner: 2
x outer B: 2
[None]
原始示例没有使用nonlocal
(请注意没有BUILD_TUPLE
和LOAD_CLOSURE
!)
>>> def closure_test():
... x = 1
... def closure():
... x = 2
... print(x)
... return closure
...
>>>
>>> import dis
>>> dis.dis(closure_test)
2 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (x)
3 4 LOAD_CONST 2 (<code object closure at 0x10d8132f0, file "<stdin>", line 3>)
6 LOAD_CONST 3 ('closure_test.<locals>.closure')
8 MAKE_FUNCTION 0
10 STORE_FAST 1 (closure)
6 12 LOAD_FAST 1 (closure)
14 RETURN_VALUE
Disassembly of <code object closure at 0x10d8132f0, file "<stdin>", line 3>:
4 0 LOAD_CONST 1 (2)
2 STORE_FAST 0 (x)
5 4 LOAD_GLOBAL 0 (print)
6 LOAD_FAST 0 (x)
8 CALL_FUNCTION 1
10 POP_TOP
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
关于字节码及简单比较
为了简化示例,去除所有名称后,它只是
>>> import dis
>>> dis.dis(lambda: print(2))
1 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 (2)
4 CALL_FUNCTION 1
6 RETURN_VALUE
其余的字节码只是在移动名称。
x
表示 1
和 2
closure
和 closure_test.<locals>.closure
表示内部函数(位于某个内存地址)
print
字面意思为打印函数
None
字面意思为单例None
特定的DIS操作码
可以使用
dis.show_code()
查看常量、名称和自由变量。
>>> dis.show_code(closure_test)
Name: closure_test
Filename: <stdin>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 3
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: 1
2: <code object closure at 0x10db282f0, file "<stdin>", line 3>
3: 'closure_test.<locals>.closure'
Variable names:
0: closure
Cell variables:
0: x
挖掘闭包本身
>>> dis.show_code(closure_test())
Name: closure
Filename: <stdin>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS, NESTED
Constants:
0: None
1: 2
Names:
0: print
Free variables:
0: x
>>> dis.show_code(lambda: print(2))
Name: <lambda>
Filename: <stdin>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
1: 2
Names:
0: print
使用 Python 3.9.10
其他相关问题