1

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

例子:

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

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

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

Python中有类似print_function的东西吗?如果没有,您将如何实施?

4

8 回答 8

4

您不能在常规控制台中执行此操作。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())。

于 2013-07-08T16:09:04.640 回答
2

这有点骇人听闻,但如果这是您经常要做的事情,您可以使用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)
于 2013-07-08T17:12:22.397 回答
1

虽然我通常同意这inspect是一个很好的答案(正如Martijn Pieters提到的那样),但我不同意您无法获得解释器中定义的对象的源代码。如果您使用dill.source.getsourcefrom dill,您可以获得函数和 lambda 的来源,即使它们是交互式定义的。它还可以从 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

>>> 
于 2014-12-13T11:47:31.817 回答
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
>>> 

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

于 2013-07-08T16:20:39.653 回答
0

你想对此有多正式?如果您的响应是“不太”的效果,那么您可以将函数的代码复制到文档字符串中,然后使用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
于 2013-07-08T16:15:47.463 回答
0

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

希望这可以帮助

于 2013-07-08T16:37:37.307 回答
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
于 2014-12-13T12:01:06.753 回答
0

我就是这样做的:

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

打印出来很好看...

于 2018-02-08T20:17:06.327 回答