获取通过 'exec' 创建的局部变量

1 投票
1 回答
70 浏览
提问于 2025-04-14 16:45

我正在尝试创建一个函数包装器,目的是自动打印出函数中每个变量的赋值情况。为此,我打算在被包装的函数代码上使用 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__)其实不是绝对必要的:因为你的函数在调用链中本来就是最后一个,所以它的值会覆盖之前收集的所有内容。不过,这个条件可以作为一个简单的检查,确保我们没有做错什么。

撰写回答