Python 中 eval'ed/dynamically 生成函数的源代码
Source code for an eval'ed/dynamically generated function in Python
我正在寻找一种在运行时从包含源代码的字符串创建 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']
它工作得很好,但该函数没有与之关联的源 code/file。这使得调试变得困难(注意未显示 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
returns 类似于 '<ipython-input-19-8efed6025c6f>'
这些类型的函数,这当然不是真实文件。有没有办法在非交互式环境中做类似的事情?
所以我能够通过深入研究 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 回溯。但是,它似乎仍然不适用于内置回溯。这对我来说已经足够了,因为我几乎总是在 IPython.
工作
编辑:请参阅下面 user2357112 的评论,了解如何在内置解释器中使用回溯打印。
我正在寻找一种在运行时从包含源代码的字符串创建 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']
它工作得很好,但该函数没有与之关联的源 code/file。这使得调试变得困难(注意未显示 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
returns 类似于 '<ipython-input-19-8efed6025c6f>'
这些类型的函数,这当然不是真实文件。有没有办法在非交互式环境中做类似的事情?
所以我能够通过深入研究 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 回溯。但是,它似乎仍然不适用于内置回溯。这对我来说已经足够了,因为我几乎总是在 IPython.
编辑:请参阅下面 user2357112 的评论,了解如何在内置解释器中使用回溯打印。