为什么这个变量引用非局部作用域不能解析?

pis*_*bee 2 python scope global-scope python-3.x spyder

这是一个寻找正整数aandb和 的最大公约数的例子a <= b。我从较小的a和一个一个的减号开始,检查它是否是两个数字的除数。

def gcdFinder(a, b):

    testerNum = a   

    def tester(a, b):
        if b % testerNum == 0 and a % testerNum == 0:
            return testerNum
        else:
            testerNum -= 1
            tester(a, b)

    return tester(a, b)

print(gcdFinder(9, 15))
Run Code Online (Sandbox Code Playgroud)

然后,我收到错误消息,

UnboundLocalError: local variable 'testerNum' referenced before assignment.

使用后global testerNum,它成功地3在Spyder控制台中显示了答案......

间谍的结果

但是在 pythontutor.com 中,它说NameError: name 'testerNum' is not defined链接)。

pythontutor的结果

Q1:在 Spyder 中,我认为这global testerNum是一个问题,因为testerNum = a它不在全局范围内。它在 function 的范围内gcdFinder。这个描述正确吗?如果是这样,Spyder 是如何给出答案的?

Q2:在pythontutor中,说最后一个截图,pythontutor中的NameError问题如何解决?

Q3:为什么Spyder和pythontutor的结果有差异,哪个是正确的?

Q4:最好不要使用global方法吗?

——

更新:Spyder 问题是由于上次运行存储的值造成的,因此它9已经定义为。这使得global testerNum工作。我已经删除了 Q1 和 Q3。

Tur*_*urn 5

问题:

在尝试解析变量引用时,Python 首先检查本地范围,然后检查任何封闭函数的本地范围。例如这段代码:

def foo():
    x=23
    def bar():
        return x +1
    return bar

print(foo()())
Run Code Online (Sandbox Code Playgroud)

将运行并打印出来,24因为 when 在 中x被引用bar,因为x在本地范围内没有它在封闭函数 ( foo)的范围内找到它。但是,一旦您尝试分配给变量,Python 就会假定它是在本地作用域中定义的。所以这:

def foo():
    x=23
    def bar():
        x = x + 1
        return x
    return bar

print(foo()())
Run Code Online (Sandbox Code Playgroud)

会抛出一个UnboundLocalError因为我试图分配给x它意味着它将在本地范围内查找,但我试图分配给它的值是基于x来自封闭范围的。由于分配将搜索限制为x本地范围,因此无法找到它并且我收到错误消息。

因此,由于testerNum -= 1else 子句中的这一行,您的错误出现了,它将搜索限制testerNum在它不存在的本地范围内。

修复:

global声明是不正确的,因为,正如你指出,testerNum没有在全球范围内定义。我对 Spyder 不熟悉,也不知道它为什么在那里工作,但它似乎以某种方式testerNum在其全局范围内获得了一个变量。

如果您使用的是 Python3,您可以通过更改您的global testerNum行来解决此问题,该行nonlocal testerNum只是告诉 Python 尽管已分配给 testerNum 并没有在本地范围内定义并继续向外搜索。

def foo():
    x=23
    def bar():
        nonlocal x
        x = x + 1
        return x
    return bar

>>> print(foo()())
24
Run Code Online (Sandbox Code Playgroud)

另一个适用于 Python 2 或 3 的选项是testerNum按照 Brambor 在他们的回答中概述的方式传递。

  • @chenghuayang 它不会消耗您可能注意到的任何处理能力。至于是不是坏形式,那当然是见仁见智了。我认为在某些情况下是有用的,但它肯定会引起一些混乱,并且作为一般做法避免可能更安全 (2认同)