为什么在Python中修改父框架只对模块框架有效?
我在玩一个叫做 inspect.stack()
的东西。我尝试去修改父级框架中的一个局部变量,结果发现只有当父级框架是在模块级别时,这个修改才有效。下面的代码展示了这个情况(Python 2.7):
import inspect
def outer():
a = 10
print a
modify()
print a
def modify():
inspect.stack()[1][0].f_locals['a'] = 8888
outer()
a = 20
print a
modify()
print a
那么,当父级框架是一个函数的时候,为什么就不行呢?我能让它工作吗?
1 个回答
模块的 f_locals
就是它的 globals()
!你可以通过在 modify
函数里面打印 is
比较和 globals()
来验证这一点:
def modify():
print(inspect.stack()[1][0].f_locals is globals())
inspect.stack()[1][0].f_locals['a'] = 8888
做了这个修改后,输出是:
$python3 modify.py
10
False
10
20
True
8888
修改 globals()
返回的字典确实有效(可以参考这个问题)。文档中明确说明模块是用一个简单的 dict
来实现它的命名空间:
模块是通过
import
语句导入的(见“导入语句”部分)。模块对象有一个由字典对象实现的命名空间(这是模块中定义的函数的func_globals
属性所引用的字典)。
但是,locals()
返回的字典不一定是本地命名空间:
注意:这个字典的内容不应该被修改;更改可能不会影响解释器使用的本地和自由变量的值。
在某些版本的 Python 2 中,当在函数内部使用 exec
语句时,修改 locals()
是有效的。试着在 outer
函数里面加上 exec ''
,看看输出是否会改变(这并不保证会改变!但可能性更大)。
补充:在 Python 2.7.6 中,如果栈帧使用 exec
语句并且代码中没有对本地变量的赋值,我可以让它工作。例如,定义 outer
如下:
def outer():
exec('a = 10')
print(a)
modify()
print(a)
locals()['a'] = 9999
print(a)
我得到的输出是:
$python2 modify.py
10
False
8888
9999
20
True
8888
但是如果我在 exec
后面加上 a = 10
,像这样:
def outer():
exec('a = 10')
a = 10
print(a)
modify()
print(a)
locals()['a'] = 9999
print(a)
结果是:
$python2 modify.py
10
False
10
10
20
True
8888
我希望这能让你明白,给本地变量赋值的条件真的很少,并且绝对不可靠。
据我所知,在 Python 3 中,locals()
返回的字典总是只是实际命名空间的一个副本,所以修改它永远不会有效。不过也没有保证。
f_locals
属性返回的值就是在那个时刻 locals()
返回的值。
总结一下:不,没有可靠的方法可以修改通用栈帧的本地命名空间。你不能修改、删除或添加新的本地变量到通用栈帧的命名空间中。