Python没有一种可移植的方法来跟踪帧…但是CPython实现可以:^{}返回一个frame对象。在
如何处理帧对象?请参阅^{}文档中的便利图表,了解它的所有有趣之处,但它们包括框架所看到的locals()和{},以及在框架中执行的代码对象,它本身包括本地名称、未绑定名称和闭包单元格。在
但是,正如其他人所指出的那样,您并不真正需要这个框架;您所需要的只是局部变量,而将它显式地传递给上下文管理器要简单得多。在
如果你真的想这么做:import contextlib
import sys
@contextlib.contextmanager
def dumping():
f = sys._getframe(2)
fl = f.f_locals.copy()
try:
yield None
finally:
for name, value in f.f_locals.items():
if name not in fl:
print('{} = {}'.format(name, value))
bar = 0
def foo():
global bar
bar = 3
baz = 4
qux = 5
with dumping():
spam = 'eggs'
eggs = 3
bar = 4
baz = 5
foo()
运行时,应打印:
^{pr2}$
换句话说,只有在with块中声明的新变量的名称和值,我想,这就是您想要的,对吗?在
如果你想要新的和反弹的本地人,你可能想存储这样的东西:fl = {(name, id(value)) for name, value in f.f_locals.items()}
当然,您也可以重新绑定非局部变量和全局变量,因此如果您关心这一点,那么也可以保存全局变量(但请确保检查locals is globals是否为模块级代码),或者执行闭包。在
如果你使用的是cpython2(为什么?对于真正的项目来说,这有时是有意义的,但要了解内部是如何工作的乐趣?但是,有些人…,同样的代码也可以工作。属性名可能稍有不同,但您可以通过转储框架和代码的dir来猜测它们。显然,您需要2.xprint语法。在
它也可以用PyPy,至少2.0b
如果你想知道我是怎么知道使用_getframe(2)的话……我没有。我很确定这会是1或2帧,可能是3帧,但是哪一个呢?所以我就这么做了:@contextlib.contextmanager
def dumping():
for i in range(4):
f = sys._getframe(i)
print(f.f_code.co_filename, f.f_code.co_firstlineno, f.f_lineno)
0当然是dumping本身;1是contextlib.contextmanager中的包装函数;2是调用框架;3是模块顶层。你想一想,这是显而易见的,但直到我知道答案,它才变得明显。:)