获取通过 'exec' 创建的局部变量
我正在尝试创建一个函数包装器,目的是自动打印出函数中每个变量的赋值情况。为此,我打算在被包装的函数代码上使用 exec() 函数,然后从执行中获取新的局部变量,并打印出这些变量的字典。
def track_assignments(func):
def wrapper(*args, **kwargs):
assignments = {}
exec(func.__code__, globals(), assignments)
for k, v in assignments.items():
print(f"{k} = {v}")
return wrapper
@track_assignments
def test():
a = 10
b = 20
c = a * b
print(a)
if __name__ == '__main__':
test()
不过,这个方法没有成功。执行代码时,assignments 字典是空的,我不太明白为什么。
我查看了其他一些问题,比如 这个,还有 exec 的文档,我按照里面的回答去做,但还是不知道自己哪里出错了。
附言:如果有人能想到更有效的方法来打印函数中所有变量的赋值,我会很感激能得到一些新的想法。
相关问题:
1 个回答
1
如果你刚开始学习Python,想要让这样的装饰器工作,可能会遇到比你想象中更复杂的问题。
我可能明天会详细补充这个回答,这里先给你一个简单粗暴的解决方案,看起来是有效的。如果你打算用它来做除了玩乐或学习以外的事情,请再考虑一下,最好不要这样做。
import pprint
import sys
def track_assignments(func):
def wrapper(*args, **kwargs):
ns = {}
def observer(frame, event, _):
nonlocal ns
if event == 'return' and frame.f_code.co_name == func.__name__:
ns = dict(frame.f_locals)
old_profile = sys.getprofile()
sys.setprofile(observer)
try:
ret = func(*args, **kwargs)
finally:
sys.setprofile(old_profile)
pprint.pprint(ns)
return ret
return wrapper
这到底是怎么回事呢?这个实现的核心是 sys.setprofile,你可以看看文档,了解更多关于它和栈帧的知识——这个话题太广泛,单靠一个回答是讲不完的。
第二个条件(frame.f_code.co_name == func.__name__)其实不是绝对必要的:因为你的函数在调用链中本来就是最后一个,所以它的值会覆盖之前收集的所有内容。不过,这个条件可以作为一个简单的检查,确保我们没有做错什么。