如何获取 Python 函数的源代码?
- 2024-12-02 08:41:00
- admin 原创
- 168
问题描述:
假设我有一个如下定义的 Python 函数:
def foo(arg1,arg2):
#do something with args
a = arg1 + arg2
return a
我可以使用 获取函数的名称foo.func_name
。如何以编程方式获取其源代码,如我上面输入的那样?
解决方案 1:
如果该函数来自文件系统上可用的源文件,那么inspect.getsource(foo)
可能会有所帮助:
如果foo
定义为:
def foo(arg1,arg2):
#do something with args
a = arg1 + arg2
return a
然后:
import inspect
lines = inspect.getsource(foo)
print(lines)
返回:
def foo(arg1,arg2):
#do something with args
a = arg1 + arg2
return a
但我相信,如果该函数是从字符串、流编译的,或者从编译文件导入的,那么您无法检索其源代码。
解决方案 2:
inspect 模块具有从 python 对象检索源代码的方法。不过,似乎只有当源位于文件中时它才有效。如果你有这个,我想你就不需要从对象中获取源代码了。
以下测试inspect.getsource(foo)
使用Python 3.6:
import inspect
def foo(arg1,arg2):
#do something with args
a = arg1 + arg2
return a
source_foo = inspect.getsource(foo) # foo is normal function
print(source_foo)
source_max = inspect.getsource(max) # max is a built-in function
print(source_max)
首先打印:
def foo(arg1,arg2):
#do something with args
a = arg1 + arg2
return a
然后失败inspect.getsource(max)
并出现以下错误:
TypeError: <built-in function max> is not a module, class, method, function, traceback, frame, or code object
解决方案 3:
伊班
注意:此答案仅适用于 IPython 和使用它的项目,例如 Jupyter。
只需使用foo??
或??foo
。
如果您使用的是 IPython,则需要键入foo??
或??foo
才能查看完整的源代码。要仅查看函数中的文档字符串,请使用foo?
或?foo
。这在 Jupyter 笔记本中也有效。
In [19]: foo??
Signature: foo(arg1, arg2)
Source:
def foo(arg1,arg2):
#do something with args
a = arg1 + arg2
return a
File: ~/Desktop/<ipython-input-18-3174e3126506>
Type: function
解决方案 4:
dis
如果你的源代码不可用,那么它就是你的朋友:
>>> import dis
>>> def foo(arg1,arg2):
... #do something with args
... a = arg1 + arg2
... return a
...
>>> dis.dis(foo)
3 0 LOAD_FAST 0 (arg1)
3 LOAD_FAST 1 (arg2)
6 BINARY_ADD
7 STORE_FAST 2 (a)
4 10 LOAD_FAST 2 (a)
13 RETURN_VALUE
解决方案 5:
虽然我一般都同意这inspect
是一个很好的答案,但我不同意您无法获取解释器中定义的对象的源代码。如果使用dill.source.getsource
from dill
,您可以获取函数和 lambda 的源代码,即使它们是以交互方式定义的。它还可以获取来自绑定或未绑定的类方法和 curry 中定义的函数的代码……但是,如果没有封闭对象的代码,您可能无法编译该代码。
>>> from dill.source import getsource
>>>
>>> def add(x,y):
... return x+y
...
>>> squared = lambda x:x**2
>>>
>>> print getsource(add)
def add(x,y):
return x+y
>>> print getsource(squared)
squared = lambda x:x**2
>>>
>>> class Foo(object):
... def bar(self, x):
... return x*x+x
...
>>> f = Foo()
>>>
>>> print getsource(f.bar)
def bar(self, x):
return x*x+x
>>>
解决方案 6:
扩展一下 runeh 的回答:
>>> def foo(a):
... x = 2
... return x + a
>>> import inspect
>>> inspect.getsource(foo)
u'def foo(a):
x = 2
return x + a
'
print inspect.getsource(foo)
def foo(a):
x = 2
return x + a
编辑:正如@0sh 指出的那样,此示例可以使用ipython
但不能使用普通的python
。但是,当从源文件导入代码时,两者都应该没问题。
解决方案 7:
由于此帖子被标记为此其他帖子的重复,因此我在此回答“lambda”的情况,尽管 OP 与 lambda 无关。
因此,对于未在其自己的行中定义的 lambda 函数:除了marko.ristin的答案之外,您可能希望使用mini-lambda或使用SymPy ,如本答案中所建议的那样。
mini-lambda
更轻量,支持任何类型的操作,但仅适用于单个变量SymPy
更重,但数学/微积分运算功能更强大。特别是它可以简化您的表达式。它还支持在同一个表达式中使用多个变量。
你可以使用以下方法进行操作mini-lambda
:
from mini_lambda import x, is_mini_lambda_expr
import inspect
def get_source_code_str(f):
if is_mini_lambda_expr(f):
return f.to_string()
else:
return inspect.getsource(f)
# test it
def foo(arg1, arg2):
# do something with args
a = arg1 + arg2
return a
print(get_source_code_str(foo))
print(get_source_code_str(x ** 2))
它正确地得出
def foo(arg1, arg2):
# do something with args
a = arg1 + arg2
return a
x ** 2
有关详细信息,请参阅mini-lambda
文档。顺便说一下,我是作者 ;)
解决方案 8:
您可以使用inspect
模块来获取完整的源代码。您必须使用模块getsource()
中的方法inspect
。例如:
import inspect
def get_my_code():
x = "abcd"
return x
print(inspect.getsource(get_my_code))
您可以在以下链接上查看更多选项。
检索您的 Python 代码
解决方案 9:
请注意,只有当 lambda 在单独的行中给出时,接受的答案才有效。如果您将其作为参数传递给函数,并希望将 lambda 的代码作为对象检索,则问题会变得有点棘手,因为inspect
会给您整行。
例如,考虑一个文件test.py
:
import inspect
def main():
x, f = 3, lambda a: a + 1
print(inspect.getsource(f))
if __name__ == "__main__":
main()
执行它会给你(注意缩进!):
x, f = 3, lambda a: a + 1
要检索 lambda 的源代码,我认为最好的办法是重新解析整个源文件(通过使用f.__code__.co_filename
)并根据行号及其上下文匹配 lambda AST 节点。
我们必须在我们的契约式设计库icontract中做到这一点,因为我们必须解析作为参数传递给装饰器的 lambda 函数。这里粘贴的代码太多了,所以看看这个函数的实现。
解决方案 10:
总结一下:
import inspect
print( "".join(inspect.getsourcelines(foo)[0]))
解决方案 11:
如果您严格自己定义函数并且它是一个相对较短的定义,那么没有依赖关系的解决方案是在字符串中定义函数并将表达式的 eval() 分配给您的函数。
例如
funcstring = 'lambda x: x> 5'
func = eval(funcstring)
然后可选择将原始代码附加到函数:
func.source = funcstring
解决方案 12:
Rafał Dowgird 的回答指出:
我相信如果该函数是从字符串、流编译的或者从编译文件导入的,那么您无法检索其源代码。
但是,可以检索从字符串编译的函数的源代码,前提是编译代码还在linecache.cache
字典中添加了一个条目:
import linecache
import inspect
script = '''
def add_nums(a, b):
return a + b
'''
bytecode = compile(script, 'unique_filename', 'exec')
tmp = {}
eval(bytecode, {}, tmp)
add_nums = tmp["add_nums"]
linecache.cache['unique_filename'] = (
len(script),
None,
script.splitlines(True),
'unique_filename',
)
print(inspect.getsource(add_nums))
# prints:
# """
# def add_nums(a, b):
# return a + b
# """
这就是attrs
库如何自动为类创建各种方法,给定一组类期望初始化的属性。请在此处查看其源代码。正如源代码所解释的那样,此功能主要用于使 PDB 等调试器能够逐步执行代码。
解决方案 13:
如果之前所有优秀的方法都失败了,你仍然陷入困境(!!),你可以尝试使你的函数崩溃并使用traceback