如何显示Python函数的代码?

时间:2013-07-08 16:05:54

标签: python

我在Python交互式控制台中定义了一个函数(不是空闲的,而不是ipython),我需要稍后再次看到它。

示例:

>>> def myf():  
...    print 1
...    print 2
...    print 3
...
>>>
>>> # a lot of scrolling
>>>  

下面的假示例输出(而不是“name'print_function'未定义”)来演示我想要的内容:

>>> print_function(myf)
myf()
    print 1
    print 2
    print 3

Python中有什么类似print_function的东西吗?如果没有,你会如何实现它?

8 个答案:

答案 0 :(得分:3)

您无法在常规控制台中执行此操作。 iPython会保留源代码的副本,以备日后再次查看时使用,但标准的Python控制台不会。

如果从文件导入了该函数,则可以使用inspect.getsource()

>>> import os.path
>>> import inspect
>>> print inspect.getsource(os.path.join)
def join(a, *p):
    """Join two or more pathname components, inserting '/' as needed.
    If any component is an absolute path, all previous path components
    will be discarded.  An empty last part will result in a path that
    ends with a separator."""
    path = a
    for b in p:
        if b.startswith('/'):
            path = b
        elif path == '' or path.endswith('/'):
            path +=  b
        else:
            path += '/' + b
    return path

但是,为了明确,inspect.getsource()对于在交互式控制台中输入的函数将失败:

>>> def foo(): pass
... 
>>> print inspect.getsource(foo)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 701, in getsource
    lines, lnum = getsourcelines(object)
  File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 690, in getsourcelines
    lines, lnum = findsource(object)
  File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 538, in findsource
    raise IOError('could not get source code')
IOError: could not get source code

因为解释器中没有任何内容保留输入(除了readline库之外,它可能会保存输入历史记录,而不是inspect.getsource()可直接使用的格式)。

答案 1 :(得分:2)

有点hackish但如果这是你将要做的很多事情,你可以使用readline模块和函数装饰器。

class PrintableFunction(object):
    """A class that contains a function and its start and end points 
    in the readline history"""

    def __init__(self, func, start, end):
        self.start = start
        self.end = end
        self.func = func

    def __call__(self, *args):
        self.func(*args)

    def __str__(self):
        """Just get all the readline history lines from start to end and
        return them"""

        lines = []
        for i in range(self.start, self.end + 1):
            lines.append(readline.get_history_item(i))
        return "\n".join(lines)


class SavedFunction(object):
    """The actual function decorator. It takes one argument, the history 
    line that at which the function definition will begin."""

    def __init__(self, start):
        """Take the starting line as an argument to the decorator. The end
        line is grabbed from the current history after the function has 
        been entered"""

        self.start = start
        self.end = readline.get_current_history_length()

    def __call__(self, func):
        return PrintableFunction(func, self.start, self.end)

您可以将这些类添加到PYTHONSTARTUP文件中,这样每次加载解释器时都可以使用它们。

>>> @SavedFunction(readline.get_current_history_length() + 1)
... def foo(bar):
...     print(bar)
>>> foo(5)
5
>>> print(foo)
def foo(bar):
    print(bar)

我为自己创建了一个自定义PS1(在我的PYTHONSTARTUP文件中),它显示了当前的readline历史记录编号,这意味着我可以快速将其添加到@saved_function参数列表中,这比获取更容易使用上面的readline.get_current_history_length函数:

[508] @SavedFunction(509)
(509) def foo(bar):
(510)     print(bar)
[511] print(foo)
def foo(bar):
    print(bar)

答案 2 :(得分:0)

你想要对此有多正式? 如果您的回答是“非常”的结果,那么您可以将函数的代码复制到docstring中,然后使用 doc

打印文档字符串

例如:

def foo():
""" Prints a sequence of numbers to the terminal:
    print 1
    print 2
"""
print 1
print 2

print foo()
--> 1
--> 2

print foo.__doc__
--> Prints a sequence of numbers to the terminal:
--> print 1
--> print 2

答案 3 :(得分:0)

当我学会了这个但每个函数都有一个代码方法时,我感到很惊讶,所以你可以尝试编写类似的东西:

>>> def f():
print "hello world" 
>>> f.__code__
<code object f at 02C57188, file "<pyshell#5>", line 1>

如您所见,它只是为您提供存储在内存中的对象的引用,而不是该函数的确切代码。但是你可以这样做:

>>> def roman():
print "Oh no!"
>>> roman.__code__ = f.__code__
>>> roman()
hello world
>>> 

我不确定,但我认为您也可以将代码写入文件并稍后从此文件中读取。你需要自己做一些研究并修补它,希望这会有所帮助。

答案 4 :(得分:0)

IPython有这个可爱的历史查找。因此,如果您知道函数的名称,则应该能够键入def myFunc(并按向上箭头键。这将使IPython调用缓冲区并向您显示与此开始匹配的最后输入的代码。在您的情况下,它将是您的函数的定义,包括完整的函数体。所以这应该为你做到!

希望这有帮助

答案 5 :(得分:0)

虽然我普遍认为inspect是一个很好的答案(如 Martijn Pieters 提到的那样),但我不同意你不能得到解释器中定义的对象的源代码。如果您使用dill中的dill.source.getsource,则可以获得函数和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

>>> 

答案 6 :(得分:0)

有很多方法可以做到这一点:

没有任何进口:

print myf.func_code.co_code 

但是你很可能最终会得到字节码。这可以通过以下方式解决: http://www.crazy-compilers.com/decompyle/

您也可以采取检查方式:

import inspect
import mymodule
print inspect.getsource(mymodule.myf)

或者您可以使用dis:

import dis

dis.dis(myf)

最后,虽然不值得推荐(安全方面),但有:

funcstring = """def myf():  
    print 1
    print 2
    print 3"""
func = eval(funcstring)

func.source = funcstring

print func.source

答案 7 :(得分:0)

我就是这样做的:

    import inspect as i
    import sys
    sys.stdout.write(i.getsource(MyFunction))

很好地打印出来......