将python函数解析为decorator中的字符串

cod*_*321 2 python decorator python-decorators python-magic

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

def foo(baz):
  bar = 1
  bar = 2
  return bar
Run Code Online (Sandbox Code Playgroud)

并将其包装到:

def foo(baz):
  bar = 1
  print 'bar: {}'.format(bar)
  bar = 2
  print 'bar: {}'.format(bar)
  return bar
Run Code Online (Sandbox Code Playgroud)

我需要使用函数作为文本,抓住"\ 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)
Run Code Online (Sandbox Code Playgroud)

并且我正在获得有价值的东西,即:

['__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>
Run Code Online (Sandbox Code Playgroud)

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

Bak*_*riu 6

首先,我建议你还是没有做这样的事情.很难获得正常工作的代码,并且很难制作出正确的版本.

此外,我真的不知道你想要做什么.该装饰器是否应该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
Run Code Online (Sandbox Code Playgroud)

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

但请注意:

  • 修改源代码时必须小心,因为创建语法无效的代码很容易(想想:多行表达式等)
  • 在编译时,你想在与原始函数相同的范围内编译.该inspect模块具有一些功能,允许您获取调用装饰器的堆栈框架,您可以从那里获取环境.请阅读此处有关如何处理堆栈的信息.
  • 源代码可能不可用.代码可以用字节码编译,原始文件可以删除.在这种情况下getsource,只会提出一个OSError.

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

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

您可能应该阅读像Python字节码黑客这样的文章,在尝试之前重新访问.它们让您了解如何查看字节码并使用它.

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


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

此功能允许您设置跟踪功能,该功能将为执行的每个"代码"调用.该函数将知道何时调用函数,输入新块等.它允许您访问将执行代码的帧,因此您应该能够找到您感兴趣的值.

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

当我有时间(可能是下周结束)时,我会尝试基于这种方法实现一些东西来演示它.

  • @jsbueno你是对的.我不知道它,它*看起来*看起来像这样做的唯一正确方法.我把它添加到我的答案中. (2认同)