Python locals() 用于包含作用域

11 投票
3 回答
5720 浏览
提问于 2025-04-17 17:06

简而言之:我想要一个可以查看外部作用域的locals()。

大家好。

我正在给一些化学家朋友教Python编程,我想确保我对作用域的理解是正确的。

考虑一下:

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

使用locals()时,它显示的是一个空的环境,而globals()则显示通常的全局变量。我该如何访问存储x的环境呢?显然,解释器知道这个变量,因为我可以引用它。

相关问题:作用域是什么时候确定的?以下的nameErrors在a = x + 2时只会在x=3的情况下出现:

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

如果你把x=3这一行注释掉,代码就能正常运行。这是否意味着Python在解释代码之前,会先对代码进行一次词法作用域的检查呢?

3 个回答

0

python3 -c "help(locals)"locals 函数的解释是这样的:

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

这意味着在 b() 函数中调用 locals() 只会显示 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 允许你设置全局变量和外部作用域的局部变量,只要你用 globalnonlocal 关键字声明它们。(我这里不详细解释这两个关键字,你可以自己查一下,应该不难。)

如果你去掉 x = 3 这一行(保留 a = x+2 这一行),你的代码会运行,因为 x 的值被使用了,但没有被设置。如果你去掉 a = x+2 这一行(保留 x = 3 这一行),你的代码也会运行,因为这会在 b() 的作用域内创建一个新的 x

如果你同时保留这两行,Python 就不知道 x 是指当前作用域外的 x(这从 a = x+2 看起来是这样的)还是指 b() 内部的 x(这从 x = 3 看起来是这样的)。

如果你想让 x 只在 b() 内部使用,那么就不应该有 a = x+2 这一行,至少在 x 被设置为 x = 3 之前不应该有。但如果你想让 xa()x,那么你应该用 nonlocal 来声明 x,像这样:

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

如果你感到困惑,记住 Python 允许你 使用 全局变量和在当前作用域外创建的变量,但前提是你不能 赋值 给它们。(通常可以读取全局变量,但设置它们是不太被推荐的。)

如果你想设置一个全局变量或非局部变量,你必须用 global(针对全局变量)或 nonlocal(针对非局部变量)来声明这些变量,就像上面的代码所示。

希望这些能帮到你。

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

我认为(请你确认一下),在第三种情况下,b()函数体内的第一行代码会在最近的作用域中查找x的值。而在这个作用域中,x是在下一行才被赋值的。如果你只有a = x+2这一行代码,最近的作用域找不到x,而是在“下一个”作用域中找到了x = 1

7

你的代码中发生的事情是,当Python看到你在b()方法中的x=3这一行时,它会在b函数内部重新创建一个x,而不是使用在a函数中已有的x

因为你的代码接着是:

    a = x+2
    x = 3

这就意味着你需要在引用x之前先定义这个在b函数内部的x

但是,当你在b函数中没有给x赋值时,Python就不会尝试创建一个新的x,也就不会报错。

下面的代码可以说明这一点:

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

    def c():
        print (x)

    b()
    c()
    print (x)

a()

不过,如果你把x声明为global,你就可以在函数中使用它,像这样:

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示例相同的结果。


如果我误解了问题:

根据对在Python中从调用命名空间获取局部变量的回答:

你可以使用:

import inspect

def a():
    x = 1

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

a()

来获取函数调用者的局部作用域。

撰写回答