代码之家  ›  专栏  ›  技术社区  ›  JaredL

python中评估/动态生成函数的源代码

  •  2
  • JaredL  · 技术社区  · 8 年前

    我正在寻找一种在运行时从包含源代码的字符串创建python函数的方法,以便通过检查获得源代码。

    我现在的方法是:

    src = 'def foo(x, y):' + '\n\t' + 'return x / y'
    g = {numpy: numpy, ...}  # Modules and such required for function
    l = {}
    exec(src, g, l)
    func = l['foo']
    

    它工作得很好,但是函数没有与之关联的源代码/文件。这使得调试困难(注意foo()中发生错误的那一行没有显示):

    >>> foo(1, 0)
    ZeroDivisionError                         Traceback (most recent call last)
    <ipython-input-85-9df128c5d862> in <module>()
    ----> 1 myfunc(3, 0)
    
    <string> in foo(x, y)
    
    ZeroDivisionError: division by zero
    

    如果我在ipython解释器中定义一个函数,我可以使用 inspect.getsource 它将在回溯中打印出来。 inspect.getsourcefile 返回类似于 '<ipython-input-19-8efed6025c6f>' 对于这些类型的函数,当然不是真正的文件。在非交互环境中有没有类似的方法?

    1 回复  |  直到 8 年前
        1
  •  2
  •   JaredL    8 年前

    所以我可以通过深入研究ipython源代码部分地解决这个问题。它利用了内置模块 linecache ,它包含从文件读取源代码和缓存结果的函数。这个 inspect traceback 模块都使用此模块来获取函数的源。

    解决方法是以与问题中相同的方式创建函数,但是使用 compile 使用组合的唯一文件名:

    source = 'def foo(x, y):' + '\n\t' + 'return x / y'
    
    filename = '<dynamic-123456>'  # Angle brackets may be required?
    code = compile(source, filename, 'exec')
    
    g = {numpy: numpy, ...}  # Modules and such required for function
    l = {}
    exec(src, g, l)
    func = l['foo']
    

    linecache 包含变量 cache 它是一个字典,将文件名映射到 (size, mtime, lines, fullname) 元组。您只需为假文件名添加一个条目:

    lines = [line + '\n' for line in source.splitlines()]
    
    import linecache
    linecache.cache[filename] = (len(source), None, lines, filename)
    

    然后函数将与 inspect.getsource() IPython的 ? / ?? 语法,在ipython回溯中。然而,它在内置的回溯中似乎仍然不起作用。这对我来说已经足够了,因为我几乎总是在伊普顿工作。

    编辑:关于如何在内置解释器中使用回溯打印,请参见下面的user2357112注释。