Python 函数内的全局关键字可见性

Python global keyword visibility inside function

当我 运行 错误地使用 CODE1 时,我正在检查 global 关键字如何为项目工作,它的工作方式出乎我的意料。 global 关键字在函数中生效,即使它不在执行部分(即在 if 条件不为真时)。

我一直在寻找有关 python 中的全局关键字的问题,但找不到对此的答复。我看到了:Python global keyword behavior, , Using global variables in a function

最有趣的是这个(但我不确定):

下面显示了我使用的代码的三个最小可重现示例:

CODE1(带全局关键字):

a = 0
def my_function():
    b=2
    if b==0:
        print("first if")
        global a
    if b==2:
        print("second if")
        a = 2
        print("func -> a", a)
        print("func -> b", b)

if __name__ == '__main__':
    my_function()
    print("main -> a", a)

结果:

second if
func -> a 2
func -> b 2
main -> a 2

CODE2(没有全局关键字):

a = 0
def my_function():
    b=2
    if b==0:
        print("first if")
    if b==2:
        print("second if")
        a = 2
        print("func -> a", a)
        print("func -> b", b)

if __name__ == '__main__':
    my_function()
    print("main -> a", a)

结果:

second if
func -> a 2
func -> b 2
main -> a 0

CODE3(带全局关键字,但反转 if 语句):

a = 0
def my_function():
    b=2
    if b==2:
        print("second if")
        a = 2
        print("func -> a", a)
        print("func -> b", b)
    if b==0:
        print("first if")
        global a

if __name__ == '__main__':
    my_function()
    print("main -> a", a)

结果:

  File "global_var_test.py", line 18
    global a
    ^
SyntaxError: name 'a' is used prior to global declaration

可以看出,if b==0: 始终为 False,if b==2: 始终为 True(打印确认)。我希望 CODE1 给出与 CODE2 相同的结果,因为 global a 不会在第一个示例中执行,因此它与省略它相同。但它给出了一个令人难以置信的结果,其中 global 关键字仍然生效并且全局变量 a 更改为值 2。在此之后,我用 CODE3 进行了测试,认为 global 关键字在所有函数中都是可见的,无论其位置如何,并且那么 CODE3 应该给出与 CODE1 相同的结果。我又错了,它的工作方式就像 global a 将被执行(然后它在分配之后引发异常)。

然后,我的最后一个问题是:¿全局关键字(可能还有其他关键字,如 nonlocal 等)在代码中是否具有可见性已编写但独立于正在执行的内容?

请帮我澄清一下。

我的 answer on this question 可能有助于理解这里的一些技术细节,尽管这是一个略有不同的问题。

简而言之,正如您发现的那样,Python 编译器基本上会根据变量在函数内部的首次使用方式来确定变量的范围;这不考虑控制语句等细节,所以如果它在看到global语句之前碰巧遇到像a = 2这样的赋值,它会判断a是一个局部变量。如果你尝试反转代码(你没有给出一个非常像这样的例子)这样编译器恰好看到 global 语句 first 它会工作(尽管仍然是错误代码):

a = 0
def my_function():
    b=2
    if b==2:
        print("second if")
        global a
        print("func -> a", a)
        print("func -> b", b)
    if b==0:
        print("first if")
        a = 2

因此,为了 practical/technical 以及风格目的,您应该始终在函数的开头而不是其他任何地方声明 global(或 nonlocal)变量。

我不确定这是语言要求还是 CPython 的细节;这将是一个有趣的后续问题。

更新: 是的,这是语言规范要求;见 https://docs.python.org/3/reference/simple_stmts.html#grammar-token-global-stmt

Names listed in a global statement must not be used in the same code block textually preceding that global statement.

这里textually preceding只是指代码的文本,而不考虑控制语句等周围的细节。这是因为 global 实际上是 解析器 的一个指令,它根据它第一次看到该变量的使用方式来确定该变量是否具有局部或全局绑定。尽管在实施细节方面可能仍然不完全准确;例如CPythonbuilds a symbol table for a code module as a separate pass over the AST returned from the parser. Thus the textual order of the code will also impact the order over which nodes in the AST are traversed. E.g. you can see where your error message came from in the visitor for global statements.