【问题标题】:parse python functions as a string within decorator将python函数解析为装饰器中的字符串
【发布时间】:2015-09-05 19:16:28
【问题描述】:

我正在尝试编写一个函数调试装饰器,它将查看:

def foo(baz):
  bar = 1
  bar = 2
  return bar

并将其包装到:

def foo(baz):
  bar = 1
  print 'bar: {}'.format(bar)
  bar = 2
  print 'bar: {}'.format(bar)
  return bar

我需要将函数当作文本来使用,以获取“\w+(?=\s*[=])”,但不知道如何访问它。我有一个从博客修改的装饰器,但我只是尝试将其更改为:

class decorator_string_check(object):

   def __init__(self, func):
        self.func = func
        wraps(func)(self)

   def __call__(self, *args, **kwargs):
        print dir(self.func)
        print dir(self.func.__code__)
        print self.func.__code__.__str__()
        ret = self.func(*args, **kwargs)
        return ret

@decorator_string_check
def fake(x):
    y = 6
    y = x
    return y

y = fake(9)

并且没有得到任何有价值的东西,即:

['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
<code object fake at 0x7f98b8b1d030, file "./logging.py", line 48>

如何使用实际的“func”文本,在其上运行正则表达式并在装饰器类对象中找到我需要的东西?谢谢

【问题讨论】:

    标签: python decorator python-decorators python-magic


    【解决方案1】:

    首先,我建议你不要这样做。很难得到有效的代码,也很难做出正确的版本。

    此外,我真的不知道您到底想做什么。这个装饰器是否应该在每次赋值后添加一个print 语句并显示更新后的值?还是只跟踪给定的变量子集?

    这就是说,要获取某些东西的源代码,您可以使用inspect 模块特别是getsource 函数:

    In [1]: def test():
       ...:     a = 1
       ...:     b = 2
       ...:     
    
    In [2]: import inspect
    
    In [3]: inspect.getsource(test)
    Out[3]: 'def test():\n    a = 1\n    b = 2\n'
    In [4]: print(inspect.getsource(test))
    def test():
        a = 1
        b = 2
    

    您可以根据需要修改和检查源代码,最后compile() 新的源代码。

    但请注意:

    • 修改源代码时必须小心,因为很容易创建语法上无效的代码(想想:多行表达式等)
    • 编译时,您希望在与原始函数相同的范围内进行编译。 inspect 模块有一些函数允许您获取调用装饰器的堆栈帧,并且您可以从那里获取环境。阅读here 了解如何处理堆栈。
    • 源代码可能不可用。代码可以编译成字节码,原始文件可能会被删除。在这种情况下,getsource 只会引发 OSError。

    一个更“理智”的解决方案是不查看源代码,而是查看字节码。您可以使用dis 模块执行此操作。您可以尝试查看变量的值何时更改并插入一些将打印该变量的字节码。

    请注意,dis 模块在 python3.4+ 中得到了极大的增强,因此对于以前的 python 版本,这可能很难。

    在尝试之前,您可能应该阅读Python bytecode hacks, gotos revisited 之类的文章。它们让您了解如何查看和使用字节码。

    这可能更安全(例如,即使机器上不存在源文件,字节码仍然可以访问),但我仍然认为您的想法不是是一件好事做,除了作为练习。


    正如 jsbueno 指出的那样,做你想做的事情的正确方法(即 python 调试器)是使用sys.settrace。

    此函数允许您设置一个跟踪函数,该函数将为每个执行的“代码”调用。该函数将知道何时调用函数,输入新块等。它使您可以访问将执行代码的框架,因此您应该能够找到您感兴趣的值。

    您应该检查lnotab_notes.txt 文件以了解作为参数提供给此函数的数据如何映射到源代码位置以了解何时执行分配。

    当我有时间(可能是下周末)时,我会尝试根据这种方法实现一些东西来演示它。

    【讨论】:

    • 实际上一个理智的解决方案(不仅仅是一个“更理智”)是使用 python 调试工具-with sys.settrace,并有效地编写一个自定义调试器,它将在每次之后比较局部变量值行,并打印不同的行——不难想出一个装饰器,它可以在装饰代码的持续时间内正确启用settrace。
    • @jsbueno 你是对的。我不知道它,它确实看起来是做类似事情的唯一正确方法。我已将其添加到我的答案中。
    • @Bakuriu doumo arigatou gozaimasu...anata wa watashi yori mo kashikoku miemasu :) 我想跟踪所有/每个变量分配,只是想知道当时的变量是什么时间。这样我就可以装饰一些不起作用的东西,而不是 1238234 打印语句方法。我相信全世界都可以使用我们的装饰器
    • "这个装饰器是否应该在每次赋值后添加打印语句并显示更新后的值?"是的,这就是我想要的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    • 2017-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多