x = 4
def test():
print(x)
x = 2
test()
这会产生错误,因为当您转到print(x)
时,它会看到您在函数x
的范围内声明了test
,并且它告诉您正在尝试在没有声明的情况下引用它。
我知道如果我做global x
这没问题,或者我移动了打印声明......我知道。
但我不明白解释器如何知道我在print语句后重新声明x
如果它一次通过代码一行。怎么知道会发生什么呢?
这显然比我所了解的更多。
答案 0 :(得分:7)
谁告诉你Python一次执行一行? Python一次执行一个字节码。该字节码来自编译器,它一次运行一个语句。语句可以是多行。函数定义是一种陈述。
因此,编译函数定义的第一步是收集在该函数体内分配的所有变量。已分配但没有global
或nonlocal
声明的任何变量都是本地变量。
(作为旁注,该函数体实际上并没有被编译成函数,它被编译成一个code
对象,它被隐藏在某个地方,只在你调用函数时运行,并进入一些字节码从function
对象构建一个code
对象,该对象在正常顺序中运行函数定义的位置运行。)
事实上,您可以通过查看其成员来查看编译器对您的函数的作用:
>>> def foo():
... global y
... x=1
... y=1
>>> foo.__code__.co_varnames
('x',)
然后,当它为你的函数体创建字节码时,co_varnames
中的所有变量都被编译成本地查找,而其余变量被编译成全局查找:
>>> dis.dis(foo)
3 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (x)
4 6 LOAD_CONST 1 (1)
9 STORE_GLOBAL 0 (y)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
答案 1 :(得分:4)
Python确实一次执行一条指令;只是函数定义是单指令。当Python解释器遇到函数定义时,它会编译函数:首先将其转换为抽象语法树(AST),然后转换为字节码。在此过程中,Python“向前看”并查看哪些变量应被视为本地变量(通过扫描AST并查看分配给但未声明为全局的名称)。
当调用该函数时,它一次执行一条指令,但编译过程可以自由地考虑整个函数,因为它被认为是单个操作。这对于各种优化也很有用。
答案 2 :(得分:1)
如果您将此程序视为三个离散操作:
x = 4 # variable assignment
def test(): foo # function definition
test() # function call
它更有意义。解释器处理函数定义 - 这需要计算变量的范围等,从而导致错误。