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

373 python function

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

def foo(arg1,arg2):
    #do something with args
    a = arg1 + arg2
    return a
Run Code Online (Sandbox Code Playgroud)

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

Raf*_*ird 499

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

如果foo定义为:

def foo(arg1,arg2):         
    #do something with args 
    a = arg1 + arg2         
    return a  
Run Code Online (Sandbox Code Playgroud)

然后:

import inspect
lines = inspect.getsource(foo)
print(lines)
Run Code Online (Sandbox Code Playgroud)

返回:

def foo(arg1,arg2):         
    #do something with args 
    a = arg1 + arg2         
    return a                
Run Code Online (Sandbox Code Playgroud)

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

  • 这个答案没有明确提到它,但inspect.getsource(foo)在单个字符串中返回源代码而不是元组,其中元组[0]是行列表.如果您需要查看repl,则getsource将更有用 (11认同)
  • 或`inspect.getsourcelines(foo)` (3认同)
  • [返回](https://docs.python.org/2/library/inspect.html#inspect.getsourcelines)一个元组; tuple [0]是表示源代码行的字符串列表,tuple [1]是运行它的执行上下文中的行号.在IPython; 这是*单元格内的行号*不是整个*笔记本* (2认同)
  • @oaklander113spect.getsource 不能像标准库中的大多数函数一样与内置函数一起使用。你可以在[他们的网站](https://www.python.org/downloads/source/)或[他们的Github](https://github.com/python/cpython)查看cpython的源代码 (2认同)

run*_*neh 177

检查模块具有用于从Python对象中检索的源代码的方法.看起来它只有在源位于文件中时才有效.如果你有,我想你不需要从对象获取源.

  • 是的,它似乎只适用于文件中定义的对象.不适用于翻译中定义的那些. (3认同)
  • 令我惊讶的是,它也适用于Ipython/Jupyter笔记本电脑 (3认同)
  • 我尝试在 `python 3.5.3` 解释器中使用检查。`import inspect` + `inspect.getsource(foo)` 工作正常。 (2认同)

sch*_*mar 81

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
Run Code Online (Sandbox Code Playgroud)

  • 祝你把这些信息显示为QToolTip();) (5认同)
  • @Noumenon因为他们在Python中通常没有源代码,所以它们是用C语言编写的 (4认同)
  • 为内置函数抛出 TypeError。 (2认同)
  • 想象一下看到“LOAD_SLOW”或“LOAD_SLOW_FOR_NO_REASON”而不是所有这些“LOAD_FAST”:) (2认同)

pra*_*nth 73

如果你使用的是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
Run Code Online (Sandbox Code Playgroud)

  • 非常有用的IPython和Jupyter笔记本如果/当你不小心删除了多个包含你刚刚花了一天时间创建和测试的函数的单元格时.... (6认同)
  • 对于谁,谁丢失了整个类:您可以通过方法恢复它:`dir(MyClass)`,然后是`MyClass.__init__??`等等。 (3认同)

Mik*_*rns 57

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

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

>>> 
Run Code Online (Sandbox Code Playgroud)

  • @ Ant6n:好吧,那只是偷偷摸摸。`dill.source.getsource` 检查解释器的函数、类、lambdas 等历史——它不检查传递给 exec 的字符串的内容。 (4认同)

Tom*_*Tom 21

为了扩展符文的答案:

>>> 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
Run Code Online (Sandbox Code Playgroud)

编辑:正如@ 0sh所指出的,这个例子可以使用ipython但不是普通的python.但是,从源文件导入代码时,两者都应该没问题.

  • 这将无法工作,因为解释器会将foo编译为字节码并丢弃源代码,如果您尝试运行`getsource(foo)`则会引发OSError. (2认同)

小智 11

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

import inspect

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

print(inspect.getsource(get_my_code))
Run Code Online (Sandbox Code Playgroud)

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


dou*_*rdo 7

总结:

import inspect
print( "".join(inspect.getsourcelines(foo)[0]))
Run Code Online (Sandbox Code Playgroud)


mar*_*tin 6

请注意,仅当 lambda 在单独的行中给出时,接受的答案才有效。如果您将它作为参数传递给函数并希望将 lambda 的代码作为对象检索,则问题会变得有点棘手,因为inspect它将为您提供整行。

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

import inspect

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

if __name__ == "__main__":
    main()
Run Code Online (Sandbox Code Playgroud)

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

    x, f = 3, lambda a: a + 1
Run Code Online (Sandbox Code Playgroud)

要检索 lambda 的源代码,在我看来,最好的办法是重新解析整个源文件(通过使用f.__code__.co_filename)并通过行号及其上下文匹配 lambda AST 节点。

我们必须在我们的按合同设计库icontract 中精确地做到这一点,因为我们必须解析作为参数传递给装饰器的 lambda 函数。代码太多这里就不贴了,看一下这个函数的实现


sma*_*rie 6

由于此帖子被标记为与其他帖子重复,因此我在这里针对“ 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))
Run Code Online (Sandbox Code Playgroud)

它正确产生

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

x ** 2
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参见mini-lambda 文档。我是作者;)


小智 5

如果您自己严格定义该函数并且它是一个相对较短的定义,则没有依赖项的解决方案是在字符串中定义该函数并将表达式的 eval() 分配给您的函数。

例如

funcstring = 'lambda x: x> 5'
func = eval(funcstring)
Run Code Online (Sandbox Code Playgroud)

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

func.source = funcstring
Run Code Online (Sandbox Code Playgroud)

  • @ninjagecko 当您向公众提供建议时,安全始终是一个问题。大多数读者来这里是因为他们在谷歌上搜索问题。我认为很多人不会逐字复制这个答案;相反,他们将采用所学到的概念并将其应用于自己的问题。 (12认同)
  • eval() 的使用让我觉得非常非常糟糕,除非您正在编写某种交互式 Python 解释器。Eval 带来了严重的安全问题。如果您采用仅评估字符串文字的策略,您仍然会失去各种有用的行为,从语法突出显示到包含评估成员的类的正确反映。 (5认同)
  • 赞成。@mehaase:安全显然不是这里的问题。你的其他评论虽然非常相关,但我想说缺乏语法突出显示是 IDE 的错误和 python 不是同像语言这一事实​​的结合。 (2认同)