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


406

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

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

我可以使用获取函数的名称foo.func_name。如上所述,我如何以编程方式获取其源代码?



1
请注意,在Python 3中,您可以使用foo.__name__
MikeyE '18

您还可以得到很多其他东西
not2qubit

Answers:


541

如果该功能来自文件系统上可用的源文件,那么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
返回一个元组;tuple [0]是代表源代码行的字符串列表,tuple [1]是在运行上下文中运行它的行号。在IPython中;这是单元内的行号,而不是整个笔记本
Red Pea 2014年

12
这个答案没有明确提及,但是inspect.getsource(foo)以单个字符串而不是元组返回源,其中tuple [0]是行列表。如果您需要查看副本,则getsource会更加有用
whaley 2015年

它不适用于例如功能len。在哪里可以找到该len函数的源代码?
oaklander114 '16

1
或者inspect.getsourcelines(foo)
斯瓦沃米尔Lenart

1
@ oaklander113 inspect.getsource不能与标准库中的大多数功能一样使用内置功能。您可以在其网站Github上
Nicolas Abril

183

检查模块具有用于从Python对象中检索的源代码的方法。貌似它仅在源位于文件中时才起作用。如果有的话,我想您就不需要从对象中获取源代码。


3
是的,它似乎仅适用于文件中定义的对象。不适用于解释器中定义的那些。
sastanin

3
令我惊讶的是,它也可以在Ipython / Jupyter笔记本中使用
Goulu博士(Dr. Goulu)2016年

1
我尝试在python 3.5.3解释器中使用inspect 。import inspect+ inspect.getsource(foo)工作正常。
安德烈C.安德森

@AndréChristofferAndersen是的,但是它对于解释器中定义的函数不起作用
某人

86

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

1
为内置函数引发TypeError。
Noumenon

8
@Noumenon,因为它们通常在Python中没有源代码,所以它们是用C编写的
schlamar

83

如果使用的是IPython,则需要输入“ foo ??”

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

9
如果/不小心删除了一个以上的单元格,而这些单元格包含刚创建和测试的一天,则在IPython和Jupyter笔记本电脑中非常有帮助....
AGS

对于谁谁失去了整个班级:您可以通过method dir(MyClass),then MyClass.__init__??等来还原它。
瓦列里

61

虽然我通常会认为这inspect是一个很好的答案,但我不同意您无法获得解释器中定义的对象的源代码。如果使用dill.source.getsourcefrom dill,即使它们是交互式定义的,也可以获取函数和lambda的来源。它也可以从咖喱中定义的绑定或未绑定类方法和函数中获取代码……但是,如果没有封闭对象的代码,您可能无法编译该代码。

>>> 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

>>> 

1
@ Ant6n:好吧,那只是偷偷摸摸。 dill.source.getsource检查解释器的历史记录中的函数,类,lambda等,而不检查传递给exec的字符串的内容。
Mike McKerns 2014年

这似乎很有趣。是否有可能使用dill回答这个问题:stackoverflow.com/questions/13827543/...
ArtOfWarfare

@ArtOfWarfare:是的,是的。 dill.source具有和的功能getnameimportable并且getsource专注于获取任何给定对象的源代码(或产生对象的可导入对象)。对于像一个简单的事情int没有源,因此如预期它不工作(即,对于“α= 10”,它返回“10”)。
Mike McKerns,2015年

但是,这确实适用于全球人员:>>> a = 10; print( [key for key, val in globals().items() if val is a][0] )
Mike McKerns,2015年

@MikeMcKerns:我已尽力不使用回答该问题dill,但我的回答仍有待改进(IE,如果您使用多个名称表示相同的值,则无法确定使用了哪个名称。如果您传递一个表达式,它不能说出那个表达式是什么。哎呀,如果您传递一个表达式,该表达式的值与一个名称相同,它将改为使用该名称。)可以dill解决我的答案中的任何缺点此处:stackoverflow.com/a/28634996/901641
ArtOfWarfare

21

扩展runeh的答案:

>>> def foo(a):
...    x = 2
...    return x + a

>>> import inspect

>>> inspect.getsource(foo)
u'def foo(a):\n    x = 2\n    return x + a\n'

print inspect.getsource(foo)
def foo(a):
   x = 2
   return x + a

编辑:正如@ 0sh所指出的,此示例使用ipython但不是plain可以工作python。但是,从源文件导入代码时,两者都应该很好。


2
这是行不通的,因为解释器会将foo编译为字节码并丢弃源代码,如果尝试运行则会引发OSError getsource(foo)
Milo Wielondek

就香草python解释器而言,@ 0sh好点。但是,以上代码示例在使用IPython时有效。
TomDotTom 2015年

11

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

import inspect

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

print(inspect.getsource(get_my_code))

您可以在下面的链接中查看更多选项。 检索您的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 文档。我是作者;)


5

请注意,只有在单独的行上给出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函数。在此处粘贴太多代码,因此请看一下此函数的实现


4

如果您要严格定义函数,并且定义相对简短,那么没有依赖性的解决方案是在字符串中定义函数并将表达式的eval()分配给函数。

例如

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

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

func.source = funcstring

2
除非您正在编写某种交互式Python解释器,否则使用eval()会让我感到非常糟糕。Eval带来了严重的安全问题。如果您只采用评估字符串文字的策略,那么您仍然会失去各种有用的行为,从语法突出显示到正确反映包含评估成员的类的范围不等。
Mark E. Haase 2012年

2
正在投票。@mehaase:安全显然不是这里的问题。您的其他评论虽然很相关,但我会说语法突出显示的缺失是IDE的错误和python不是同调语言的事实的结合。
ninjagecko 2012年

7
当您向公众提供建议时,@ ninjagecko安全始终是一个问题。大多数读者之所以来这里,是因为他们在搜寻问题。我认为没有多少人会逐字复制此答案;相反,他们将采用他们学到的概念并将其应用于自己的问题。
Mark E. Haase 2012年

4

总结一下:

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

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.