考虑此功能:
def quux():
i = 42
print([i for x in [1]])
结果:[42]
因此,我假设本地变量在列表推导中可见。
现在考虑一下:
def foo():
return currentframe().f_back.f_locals["i"]
def quux():
i = 42
print([foo() for x in [1]])
结果:
KeyError:'i'
检查堆栈帧,结果发现在quux和foo的帧之间插入了一个额外的堆栈:
{'x': 1, '.0': <tuple_iterator object at 0x7f59eb94c860>}
好的,很公平。尽管令我感到困惑的是,为什么第一个示例看到了i
。如果还有一个额外的堆栈框,它应该是不可见的,不是吗?
并且从更实际的角度出发,无论我的函数是从列表理解中获取还是从多个嵌套列表理解中获取,无论如何,我如何才能获得调用函数的堆栈框架?
答案 0 :(得分:1)
这是一个很好的观察结果(无论其有用性如何:))。回答问题的最好方法是亲自查看:转储反汇编(使用dis.dis(quux)
的两个版本)。您会注意到两个版本的重要区别是在第一个版本中加载了闭包。这是因为您在列表理解对象中引用了变量i,并且这样做使i成为闭包的一部分,并且列表comp可以访问它。在第二种情况下,没有这样的事情,所以您会得到该错误。
对于第二部分,现在您已经了解了它,也许您想重新措辞?
答案 1 :(得分:1)
给出变量可见性的是变量 scope ,而不是可能的帧。 Python局部变量具有函数作用域,因此在第一个示例中,i
函数的任何行中都可见quux
变量。
框架只是CPython实现的细节。来自inspect
模块的标准库文档:
inspect.currentframe()
返回调用者堆栈框架的框架对象。CPython实现细节:此函数依赖于解释器中的Python堆栈框架支持,并不能保证在所有Python实现中都存在。如果在没有Python堆栈框架支持的实现中运行,此函数将返回None。
当实现使用Python堆栈框架支持时,该实现将具有用于显式函数调用的框架,并且可能会添加交织框架以供内部使用(此处用于列表理解),但是可能还有其他用例。
因此,您有2种可能的选择:
实用的:您已经发现在该用例中只有一个交错帧,因此您可以跳过它:
def foo():
return currentframe().f_back.f_back.f_locals["i"]
明智的文档编制:
您知道外部框架将包含一个i
局部变量。只需扫描外部框架中包含它的第一个框架即可:
def foo():
for f in getouterframes(currentframe()):
if 'i' in f.frame.f_locals:
return f.frame.f_locals['i']
return None