我正在阅读一本维基书Python_Programming,我对下面的代码有点困惑:
def foo():
def bar():
x=5
return x+y
y=10
return bar()
foo()
好吧,我注意到y
是在bar()之外定义的,它在x+y
中使用,它是在y
之前定义的。我认为类似的代码会导致C中的编译错误,我们必须编写如下内容:
def foo():
def bar(y):
x=5
return x+y
y=10
return bar(y)
foo()
如果没有将y
定义为bar()
中的形式参数,我认为编译器没有问题。
但是我觉得它在Python中完全没问题,这是一种解释型语言,对吗?
与编译语言相比,这在解释语言方面有所不同吗?在解释顶部的代码时,Python使用的实际过程是什么?
编辑1: 我认为下面的答案已经明确表达了这一点,它是关于自由变量和闭包的。以下是一些我认为可以帮助解决这个问题的链接:
SO:python-free-variables-why-does-this-fail
SO:closures-in-python
答案 0 :(得分:2)
你正在看closure; Python编译器将y
中的bar
标记为自由变量,因为y
未分配给。{/ p>
y
是foo
中的本地,并且因为有一个嵌套作用域使用该名称作为自由变量,y
被标记为闭包
当代码运行时,Python在创建y
函数时立即为bar()
创建一个闭包(每次调用foo()
时都会重新创建;只有函数的字节码保持不变,一个附加到foo
功能代码的常量。)
只有当bar()
被调用时,Python才需要查找y
,这意味着取消引用该闭包,并从那里取消引用y
的当前值
您可以通过字节码的内省和反汇编来看到所有这些:
>>> import dis
>>> def foo():
... def bar(): return y
... y = 10
... return bar
...
>>> foo()
<function foo.<locals>.bar at 0x10d53ce60>
>>> foo()()
10
这构建了一个带闭包的函数,并在不调用的情况下返回它。这允许我们内省bar()
创建的foo()
函数。
>>> dis.dis(foo)
2 0 LOAD_CLOSURE 0 (y)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object bar at 0x10d5138a0, file "<stdin>", line 2>)
9 LOAD_CONST 2 ('foo.<locals>.bar')
12 MAKE_CLOSURE 0
15 STORE_FAST 0 (bar)
3 18 LOAD_CONST 3 (10)
21 STORE_DEREF 0 (y)
4 24 LOAD_FAST 0 (bar)
27 RETURN_VALUE
注意Python编译器如何在顶部插入LOAD_CLOSURE
y
。 MAKE_CLOSURE
为bar
创建带附加闭包的函数对象。
>>> dis.dis(foo())
2 0 LOAD_DEREF 0 (y)
3 RETURN_VALUE
所有bar()
所要做的就是取消引用附加的闭包。
>>> foo.__code__.co_consts
(None, <code object bar at 0x10d5138a0, file "<stdin>", line 2>, 'foo.<locals>.bar', 10)
>>> foo.__code__.co_cellvars
('y',)
>>> foo().__closure__
(<cell at 0x10d5e2c20: int object at 0x10d188940>,)
>>> foo().__closure__[0].cell_contents
10
结果指向y
,正如预期的那样,查找内容会为您提供10
。
编译语言也可以这样做;有人设法将Scheme编译为C,并保留了闭包:Compiling Scheme to C,例如。