为什么这个对非本地范围的变量引用不会解析?

Why won't this variable reference to a non-local scope resolve?

下面是一个例子,用于查找正整数 ab 以及 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))

然后,我收到错误消息,

UnboundLocalError: local variable 'testerNum' referenced before assignment.

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

但在pythontutor.com中,它表示NameError: name 'testerNum' is not definedlink)。

Q1: 在 Spyder 中,我认为 global testerNum 是一个问题,因为 testerNum = a 不在全局范围内。它在函数 gcdFinder 的范围内。这个描述是否正确?如果有,Spyder 是如何显示答案的?

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

Q3:为什么Spyder和pythontutor的结果不一样,哪个是正确的?

Q4:是不是最好不用global方法?

--

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

Q2Q4 的答案。

正如我在评论中所写,您可以将 testerNum 解析为参数。

您的代码将如下所示:

def gcdFinder(a, b):

    testerNum = a   

    def tester(a, b, testerNum):
        if b % testerNum == 0 and a % testerNum == 0:
            return testerNum
        else:
            testerNum -= 1
            return tester(a, b, testerNum)  # you have to return this in order for the code to work

    return tester(a, b, testerNum)

print(gcdFinder(9, 15))

编辑:(见评论)

只打第4题,是的,最好不要用globalglobal 通常突出糟糕的代码设计。

你会遇到很多麻烦;我强烈建议您查找计算 GCD 的标准方法,并改用 Euclid 算法。编码细节留给学生作为练习。

问题:

尝试解析变量引用时,Python 首先检查局部作用域,然后检查任何封闭函数的局部作用域。例如这段代码:

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

print(foo()())

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

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

print(foo()())

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

所以你的错误出现是因为你的 else 子句中的 testerNum -= 1 行,它将 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

在 Python 2 或 3 中可行的另一个选项是绕过 testerNum,正如 Brambor 在他们的回答中概述的那样。