4
x = 4
def test():
    print(x)
    x = 2
test()

这会产生一个错误,因为当您转到 时print(x),它会看到您已x在函数的范围内声明test,并且它告诉您您正在尝试引用它而没有声明它。

我知道如果我这样做global x没问题,或者如果我移动打印语句......我知道。

但是我不明白解释器是如何知道我x在 print 语句之后重新声明的,如果它一次通过代码行。它怎么知道会发生什么?

这显然比我所知道的要多。

4

3 回答 3

7

谁告诉你 Python 一次执行一行?Python 一次执行一个字节码。该字节码来自编译器,它一次运行一条语句。语句可以是多行。函数定义是一个语句。

因此,编译函数定义的第一步是收集在该函数体内分配的所有变量。任何已分配但没有globalornonlocal声明的变量都是局部变量。

(作为旁注,该函数体实际上并未编译为函数,它被编译为一个对象,该对象被隐藏在某处,并且仅在您调用该函数时运行,并编译为一些从该对象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        
于 2013-10-04T01:01:56.513 回答
4

Python 确实一次执行一条指令。只是函数定义是一条指令。当 Python 解释器遇到函数定义时,它会编译该函数:首先将其转换为抽象语法树 (AST),然后再转换为字节码。正是在这个过程中,Python“向前看”并查看哪些变量应该被视为本地变量(通过扫描 AST 并查看分配了哪些名称但未声明为全局变量)。

当函数被调用时,它一次执行一条指令,但编译过程可以自由地考虑整个函数,因为它被认为是单个操作。这对于各种优化也很有用。

于 2013-10-04T01:01:43.283 回答
1

如果您将此程序视为三个离散操作:

x = 4           # variable assignment 
def test(): foo # function definition
test()          # function call

这更有意义。解释器处理函数定义 - 这需要确定变量的范围等,因此您的错误。

于 2013-10-04T01:03:45.290 回答