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

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

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

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

Suppose I have a Python function as defined below:

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

I can get the name of the function using foo.func_name. How can I programmatically get its source code, as I typed above?


回答 0

如果该功能来自文件系统上可用的源文件,那么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                

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

If the function is from a source file available on the filesystem, then inspect.getsource(foo) might be of help:

If foo is defined as:

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

Then:

import inspect
lines = inspect.getsource(foo)
print(lines)

Returns:

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

But I believe that if the function is compiled from a string, stream or imported from a compiled file, then you cannot retrieve its source code.


回答 1

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

The inspect module has methods for retrieving source code from python objects. Seemingly it only works if the source is located in a file though. If you had that I guess you wouldn’t need to get the source from the object.


回答 2

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

dis is your friend if the source code is not available:

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

回答 3

如果使用的是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

If you are using IPython, then you need to type “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

回答 4

虽然我通常会认为这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

>>> 

While I’d generally agree that inspect is a good answer, I’d disagree that you can’t get the source code of objects defined in the interpreter. If you use dill.source.getsource from dill, you can get the source of functions and lambdas, even if they are defined interactively. It also can get the code for from bound or unbound class methods and functions defined in curries… however, you might not be able to compile that code without the enclosing object’s code.

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

>>> 

回答 5

扩展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。但是,从源文件导入代码时,两者都应该很好。

To expand on runeh’s answer:

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

EDIT: As pointed out by @0sh this example works using ipython but not plain python. It should be fine in both, however, when importing code from source files.


回答 6

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

import inspect

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

print(inspect.getsource(get_my_code))

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

You can use inspect module to get full source code for that. You have to use getsource() method for that from the inspect module. For example:

import inspect

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

print(inspect.getsource(get_my_code))

You can check it out more options on the below link. retrieve your python code


回答 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 文档。我是作者;)

Since this post is marked as the duplicate of this other post, I answer here for the “lambda” case, although the OP is not about lambdas.

So, for lambda functions that are not defined in their own lines: in addition to marko.ristin‘s answer, you may wish to use mini-lambda or use SymPy as suggested in this answer.

  • mini-lambda is lighter and supports any kind of operation, but works only for a single variable
  • SymPy is heavier but much more equipped with mathematical/calculus operations. In particular it can simplify your expressions. It also supports several variables in the same expression.

Here is how you can do it using 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))

It correctly yields

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

x ** 2

See mini-lambda documentation for details. I’m the author by the way ;)


回答 8

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

Please mind that the accepted answers work only if the lambda is given on a separate line. If you pass it in as an argument to a function and would like to retrieve the code of the lambda as object, the problem gets a bit tricky since inspect will give you the whole line.

For example, consider a file test.py:

import inspect

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

if __name__ == "__main__":
    main()

Executing it gives you (mind the indention!):

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

To retrieve the source code of the lambda, your best bet, in my opinion, is to re-parse the whole source file (by using f.__code__.co_filename) and match the lambda AST node by the line number and its context.

We had to do precisely that in our design-by-contract library icontract since we had to parse the lambda functions we pass in as arguments to decorators. It is too much code to paste here, so have a look at the implementation of this function.


回答 9

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

例如

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

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

func.source = funcstring

If you’re strictly defining the function yourself and it’s a relatively short definition, a solution without dependencies would be to define the function in a string and assign the eval() of the expression to your function.

E.g.

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

then optionally to attach the original code to the function:

func.source = funcstring

回答 10

总结一下:

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

to summarize :

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

回答 11

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

I believe that variable names aren’t stored in pyc/pyd/pyo files, so you can not retrieve the exact code lines if you don’t have source files.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。