假设我有一个如下定义的Python函数:

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

我可以使用foo.func_name获取函数的名称。我如何通过编程获得它的源代码,就像我上面键入的那样?


当前回答

虽然我通常同意inspect是一个很好的答案,但我不同意您不能获得解释器中定义的对象的源代码。如果从dill中使用dill.source.getsource,则可以获得函数和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

>>> 

其他回答

拉法łDowgird朋友与答案:

我相信,如果函数是从字符串、流或从编译文件导入的,那么你就不能检索它的源代码。

但是,可以检索从字符串编译的函数的源代码,前提是编译代码还向lineecache添加了一个条目。缓存dict类型:

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)能够逐级遍历代码的特性。

你可以使用inspect模块来获得完整的源代码。你必须从inspect模块中使用getsource()方法。例如:

import inspect

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

print(inspect.getsource(get_my_code))

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

请注意,只有在单独的一行上给出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节点。

我们必须在契约式设计库契约中做到这一点,因为我们必须解析作为参数传递给装饰器的lambda函数。这里粘贴的代码太多了,所以看一下这个函数的实现。

只是使用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

inspect模块有从python对象中检索源代码的方法。但是,似乎只有当源文件位于文件中时,它才有效。如果你有这个,我猜你就不需要从对象中获取源了。


下面使用Python 3.6测试inspect.getsource(foo):

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