Python locals() 用于包含作用域
简而言之:我想要一个可以查看外部作用域的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 个回答
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 允许你设置全局变量和外部作用域的局部变量,只要你用 global
和 nonlocal
关键字声明它们。(我这里不详细解释这两个关键字,你可以自己查一下,应该不难。)
如果你去掉 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
之前不应该有。但如果你想让 x
是 a()
的 x
,那么你应该用 nonlocal
来声明 x
,像这样:
def a():
x = 1
def b():
nonlocal x # use a()'s x
a = x+2
x = 3
b()
如果你感到困惑,记住 Python 允许你 使用 全局变量和在当前作用域外创建的变量,但前提是你不能 赋值 给它们。(通常可以读取全局变量,但设置它们是不太被推荐的。)
如果你想设置一个全局变量或非局部变量,你必须用 global
(针对全局变量)或 nonlocal
(针对非局部变量)来声明这些变量,就像上面的代码所示。
希望这些能帮到你。
这段话的意思是,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
。
你的代码中发生的事情是,当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()
来获取函数调用者的局部作用域。