【问题标题】:Override REPL output覆盖 REPL 输出
【发布时间】:2018-05-31 16:43:21
【问题描述】:

我正在寻找一种方法来覆盖和解析 Python REPL 中的所有输出:例如终端中的 python/IPython、qtconsole。

通过覆盖打印功能,这对于打印文本很简单。举个简单的例子,假设我们想在所有输出中添加一个感叹号:

orig_print = print
print = lambda text: orig_print(text + '!')

现在所有打印命令都将添加感叹号。这可以通过以下方式重置:

del print

我的问题:如何对 REPL 输出进行等效操作?例如,我怎样才能使它起作用?

In[1]: 5 + 5
Out[2]: 10!

搜索使我走上了 contextlib、subprocess 和 sys.stdout 的道路,但我还没有找到解决方案。在 Github 上检查了 sympy 的打印模块,没有成功。

【问题讨论】:

  • 嗯..也许sys.settrace可以做到这一点

标签: python ipython stdout read-eval-print-loop


【解决方案1】:

我刚刚尝试覆盖 sys.stdout.write 并且它有效(有一些怪癖)。如果我错了,有人会纠正我,但我认为不会比这更好。

In [1]: import sys

In [2]: tmp = sys.stdout.write

In [3]: sys.stdout.write = lambda text: tmp(text + '!')

In [4]: 5 + 5
!Out[4]: 10!
!!
!!In [5]:

编辑:
我已经走到这一步了。还没弄清楚那 1 个额外的 ! 来自哪里。

In [5]: sys.stdout.write = lambda text: tmp(text if text.endswith('\n') else text + '!\r')

In [6]: 5+5
Out[6]: 10!
!
In [7]:

【讨论】:

  • 可以修复下一行的!tmp(text + '!') 更改为tmp(text + '!\r')
  • 对于我的特定用例,这个异常(额外的!)没有出现:您的解决方案按原样工作!
  • 有没有人对 IPython qtconsole 中的等价物有想法?它似乎不使用 sys.stdout。我尝试过 ipykernel.iostream.OutStream.write 和 IPython.sys.stdout.write。
【解决方案2】:

一个在 IPython QtConsole 中工作的示例,基于 this article。这与 orangeink 的覆盖 stdout 的解决方案结合使用:

class SciNum:
    """For compatibility with IPython's pretty printer: Contains a string,
    with a REPR that allows pretty() to print without quotes, as it would
    if using the string directly."""
    def __init__(self, text: str):
        self.text = text

    def __repr__(self):
        return self.text


def _print_ipython(arg, p, cycle) -> None:
    """Uses IPython's pretty printer to modify output for a qtconsole or notebook;
    stdout doesn't seem to work for them."""
    p.text(IPython.lib.pretty.pretty(SciNum(format(arg))))

def start() -> None:
    if not ipython_exists:
        return

    ip = IPython.get_ipython()
    # We only need to handle IPython separately if in a Qtconsole or Notebook.
    if isinstance(ip, IPython.terminal.interactiveshell.TerminalInteractiveShell):
        return

    text_formatter = ip.display_formatter.formatters['text/plain']

    text_formatter.for_type(float, _print_ipython)
    text_formatter.for_type(int, _print_ipython)

【讨论】:

    【解决方案3】:

    标准的 REPL 调用 sys.displayhook 来完成它的工作:https://docs.python.org/3/library/sys.html#sys.displayhook

    简单示例:

    >>> import sys
    >>> def displayhook(value):
    ...     if value is None: return
    ...     sys.stdout.write(repr(value) + "!\n")
    ... 
    >>> sys.displayhook = displayhook
    >>> 1 + 1
    2!
    

    请注意,这不适用于 ipython,但它适用于默认 REPL(通常只需调用 python3 即可获得)以及 IDLE (python3 -m idlelib.idle)。

    【讨论】:

      猜你喜欢
      • 2016-11-19
      • 2020-11-19
      • 2011-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-04
      相关资源
      最近更新 更多