如何获取Python函数的源代码?

How can I get the source code of a Python function?

假设我有一个python函数,定义如下:

1
2
3
4
def foo(arg1,arg2):
    #do something with args
    a = arg1 + arg2
    return a

我可以使用foo.func_name获得函数的名称。如何以编程方式获取其源代码,正如我在上面键入的那样?


如果函数来自文件系统上可用的源文件,那么inspect.getsource(foo)可能有帮助:

如果foo定义为:

1
2
3
4
def foo(arg1,arg2):        
    #do something with args
    a = arg1 + arg2        
    return a

然后:

1
2
3
import inspect
lines = inspect.getsource(foo)
print(lines)

返回:

1
2
3
4
def foo(arg1,arg2):        
    #do something with args
    a = arg1 + arg2        
    return a

但我相信,如果函数是从字符串、流或从编译文件导入的,那么您就无法检索其源代码。


inspect模块具有从python对象检索源代码的方法。不过,似乎只有当源位于文件中时,它才起作用。如果你有,我想你不需要从对象中获取源代码。


如果源代码不可用,dis是您的朋友:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 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


如果您使用的是ipython,那么您需要键入"foo"?"

1
2
3
4
5
6
7
8
9
10
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


虽然我一般认为inspect是一个很好的答案,但我不同意您不能获得解释器中定义的对象的源代码。如果使用dill中的dill.source.getsource,则可以获取函数和lambda的源,即使它们是交互定义的。它还可以从课程表中定义的绑定或未绑定类方法和函数中获取代码。但是,如果没有封闭对象的代码,您可能无法编译该代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> 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

>>>


扩展runeh的答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> 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。但是,当从源文件导入代码时,两者都可以。


您可以使用inspect模块获取完整的源代码。您必须使用getsource()方法来处理来自inspect模块的方法。例如:

1
2
3
4
5
6
7
import inspect

def get_my_code():
    x ="abcd"
    return x

print(inspect.getsource(get_my_code))

您可以在下面的链接中查看更多选项。检索您的python代码


总结:

1
2
import inspect
print("".join(inspect.getsourcelines(foo)[0]))

如果您自己严格定义函数,而且定义相对较短,那么没有依赖关系的解决方案将是在字符串中定义函数,并将表达式的eval()赋给您的函数。

例如。

1
2
funcstring = 'lambda x: x> 5'
func = eval(funcstring)

然后可以选择将原始代码附加到函数:

1
func.source = funcstring


请注意,只有当lambda在单独的行上给出时,接受的答案才有效。如果您将它作为一个参数传递给一个函数,并且希望将lambda的代码作为对象来检索,那么这个问题会变得有点棘手,因为inspect将给出整行代码。

例如,考虑一个文件test.py

1
2
3
4
5
6
7
8
import inspect

def main():
    x, f = 3, lambda a: a + 1
    print(inspect.getsource(f))

if __name__ =="__main__":
    main()

执行它会给你(注意缩进!):

1
    x, f = 3, lambda a: a + 1

为了检索lambda的源代码,我认为最好的办法是重新解析整个源文件(使用f.__code__.co_filename),并根据行号及其上下文匹配lambda ast节点。

我们必须在契约库IContract的设计中做到这一点,因为我们必须解析lambda函数,并将其作为参数传递给修饰符。这里要粘贴的代码太多,请看一下这个函数的实现。


因为这篇文章被标记为另一篇文章的副本,所以我在这里回答"lambda"这个问题,尽管OP不是关于lambdas的。

因此,对于没有在自己的行中定义的lambda函数:除了marko.ristin的答案之外,您可能希望使用mini lambda或使用此答案中建议的sympy。

  • mini-lambda更轻,支持任何类型的操作,但只适用于单个变量。
  • SymPy较重,但更配备数学/微积分运算。特别是它可以简化表达式。它还支持同一表达式中的多个变量。

以下是使用mini-lambda的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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))

它正确地产生

1
2
3
4
5
6
def foo(arg1, arg2):
    # do something with args
    a = arg1 + arg2
    return a

x ** 2

详见mini-lambda文件。顺便说一下,我是作者;)


如果使用ipython,另一个选项是在函数名后使用??查看其源代码。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
[nav] In [1]: def foo(arg1,arg2):
         ...:     #do something with args
         ...:     a = arg1 + arg2
         ...:     return a

[nav] In [2]: foo??
Signature: foo(arg1, arg2)
Docstring: <no docstring>
Source:
def foo(arg1,arg2):
    #do something with args
    a = arg1 + arg2
    return a

我相信变量名不会存储在pyc/pyd/pyo文件中,因此如果没有源文件,就无法检索准确的代码行。