在Python中读取父级作用域
在Python中读取父级作用域
假设我有一个函数的层次结构,我希望能够访问(而不是更改!)父级的作用域。下面是一个例子。
假设我有一个函数的层次结构,我希望能够访问(而不是更改!)父级的作用域。这是一个说明性的例子。
def f():
a = 2
b = 1
def g():
b = 2
c = 1
print globals() # 包含 a=1 和 d=4
print locals() # 包含 b=2 和 c=1,但没有 a
print dict(globals(), **locals()) # 包含 a=1, d=4(来自全局变量),b=2 和 c=1(来自 g)
# 我希望 a=2 和 b=1(来自 f),d=4(来自全局变量),没有 c
g()
a = 1
d = 4
f()
我能够从 g 函数中访问 f 函数的作用域吗?
在Python中,通常情况下你是无法读取父作用域的。如果你的Python实现支持堆栈帧(CPython支持),你可以使用inspect
模块检查调用函数的帧,并提取局部变量,但我怀疑这不是解决你想解决的问题的最佳方法(无论那个问题是什么)。如果你认为你需要这样做,那么你的设计可能存在一些缺陷。
需要注意的是,使用inspect
可以让你上溯调用堆栈,而不是上溯词法作用域的堆栈。如果你从f()
返回g
,那么f
的作用域将消失,因此根本没有办法访问它,因为它甚至都不存在。
这是由于安全问题而不能这样做吗?我觉得作用域非常微妙/令人困惑,但是一旦你从f
中调用g
,f
的作用域肯定仍然存在,因为之后你又回到f
中执行代码!我思考这个问题的原因是为了进行单元测试,测试副作用... :s
不,这不是出于安全原因。正如我所说,如果你的Python实现支持堆栈帧,你是可以上溯调用堆栈的——只需使用inspect.currentframe().f_back.f_locals
这样的代码来获取调用帧的局部变量,这个帧将是你示例中的f
的一个实例。你无法访问词法作用域的变量是因为这个概念本身就是没有意义的。在调用堆栈中可能存在多个f
的实例,它们都有不同的局部变量,或者可能根本没有实例。
你不应该这样做的原因是违背了词法作用域的核心思想,与动态作用域相对立的编程语言(几乎所有编程语言)是不支持动态作用域的。详细信息请参见en.wikipedia.org/wiki/…。
我觉得让我感到困惑的是g
确实可以访问f
的变量(例如:e.g.)...但我们只是无法显示它们。
g()
无法访问f
的b
,除非你使用堆栈帧introspection,因为g
的b
将会隐藏f
的b
。一旦你在闭包中引用了f
的某些变量,它们就会附加到闭包中,所以你可以通过introspection访问它们。但是你的代码实际上并没有访问f
的任何名称,因此它不会生成闭包。
啊哈!这就是我之前没有理解的部分(它们只在被查找后才会附加)。如果我们能够遍历所有“可能”的变量,那就完成了...但是我们不知道它们是什么!如果你在函数f
中定义f_local_state = locals().keys()
,那么在g
中你可以遍历f
的作用域,因为你可以在g
中访问f_local_state
。