9

TL;DR:我想要一个在包含范围内查找的 locals()。

大家好。

我正在向一些化学家朋友教授 Python 编程课程,我想确定我真的了解范围。

考虑:

def a():
    x = 1
    def b():
        print(locals())
        print(globals())
    b()

Locals 打印一个空的环境,而 globals 打印通常的全局变量。如何访问存储 x 的环境?很明显,解释器知道它,因为我可以参考它。

相关:范围界定何时发生?仅当包含 x=3 时,a = x+2 上的以下名称错误:

def a():
    x = 1
    def b():
        a = x+2
        x = 3
    b()

如果您注释掉 x=3,则代码有效。这是否意味着 python 在解释代码之前对代码进行了词法范围传递?

4

3 回答 3

6

您的代码中发生的情况是,当 python see 是x=3您的方法中的行时b(),它在函数内使用x范围重新创建,b而不是在函数中使用x它的范围a

因为你的代码然后去:

    a = x+2
    x = 3

它是说你需要x在引用它之前定义内部范围。

但是,当您没有xb函数中分配 时,python 不会尝试创建较低的范围x,也不会引发任何错误。

下面的代码将说明这一点:

def a():
    x = 1
    def b():
        x = 3
        print (x)

    def c():
        print (x)

    b()
    c()
    print (x)

a()

但是,如果您要声明xglobal可以在函数中使用它,如下所示:

def a():
    global x
    x = 1

    def d():
        global x
        x += 2

    print (x)
    d()
    print (x)

a()

Python 3 还添加了一个nonlocal关键字,可让您从封闭范围访问变量,用法如下:

def a():
    x = 1

    def d():
        nonlocal x
        x += 2

    print (x)
    d()
    print (x)

a()

将打印与示例相同的结果global


万一我错过了这个问题:

根据Get locals from calling namespace in Python的回答:

您可以使用:

import inspect

def a():
    x = 1

    def d():
        frame = inspect.currentframe()
        try:
            print (frame.f_back.f_locals)
        finally:
            del frame
    d()

a()

获取函数调用者的本地范围。

于 2013-02-25T22:36:15.330 回答
1

该语句print(locals())指的是最近的封闭范围,即def b():函数。调用 时b(),您将打印此 b 函数的局部变量,并且 x 的定义超出范围。

def a():
    x = 1
    def b():
        print(locals())
        print(globals())
    b()
    print(locals())

将 x 作为局部变量打印。

对于你的第二个问题:

def a():
    x = 1
    def b():
        #a = x+2
        x = 3
    b()

>>> a()

没有错误。

def a():
    x = 1
    def b():
        a = x+2
        #x = 3
    b()

>>> a()

没有错误。

def a():
    x = 1
    def b():
        a = x+2
        x = 3
    b()

>>> a()

给出以下错误:UnboundLocalError: local variable 'x' referenced before assignment

IMO(请检查),在这第三种情况下,正文中的第一条语句在最封闭的范围内b()查找 x 的值。在这个范围内,x 被分配在下一行。如果您只有 statement ,则 x 在大多数封闭范围中找不到,而是在“下一个”封闭范围中找到,带有.a = x+2x = 1

于 2013-02-25T22:27:17.903 回答
0

python3 -c "help(locals)"说这个locals功能:

locals()
    Return a dictionary containing the current scope's local variables.

这意味着调用只会显示哪些变量是本地locals()的。这并不意味着不能看到其范围之外的变量——而是只返回本地变量的信息。b()b()b()locals()b()

您可能想要的是b()显示有关调用函数的变量的信息。为此,请使用以下代码(显示的b()局部变量):

def a():
    x = 1
    def b():
        print(locals())
    b()

并用这个替换它(它使用inspect模块来获取调用框架的本地信息):

def a():    
    x = 1
    def b():
        import inspect
        print(inspect.currentframe().f_back.f_locals)
    b()

至于你的其他问题:

def a():
    x = 1
    def b():
        a = x+2
        x = 3
    b()

您得到的错误是因为 Python 3 允许您在外部范围内设置/分配全局变量和局部变量,前提是您使用globalandnonlocal关键字声明它们。(我不会在这里解释它们;我只会说你自己查找它们不会有任何问题。)

如果您删除该x = 3行(保留该a = x+2行),您的代码将运行,因为x' 的值正在被使用,但未设置。相反,如果您删除该a = x+2行(保留该x = 3行),您的代码将运行,因为它将创建一个新的xinsideb()范围。

如果您保留这两行,Python 不知道是否x应该引用x当前范围之外的内容(a = x+2似乎暗示)或者它是否应该引用x本地 tob()的范围(正如x = 3暗示的那样)。

如果你想x成为本地的b(),那么你不应该在a = x+2那里有那条线,至少在之前x没有设置x = 3。但是如果你想x成为a()'s x,那么你应该声明xnonlocalb()像这样:

def a():
    x = 1
    def b():
        nonlocal x  # use a()'s x
        a = x+2
        x = 3
    b()

如果您感到困惑,请记住 Python 将允许您使用全局变量和在当前范围之外创建的变量,但前提是您不分配给它们。(读取全局变量通常是可以接受的,但设置它们是不受欢迎的。)

如果要设置全局变量或非局部变量,则必须使用global(for global variables) 或nonlocal(for non-local variables) 声明这些变量,如上面的代码所示。

我希望这有帮助。

于 2020-02-28T19:09:28.057 回答