更新:
我是否正确地相信 Python 2 的 print 语句的硬连线意味着这种增强不能用标准解释器来实现?
不,打印语句的重要部分根本没有硬连线。print 只是写入 sys.stdout,它可以是任何带有write
和flush
方法的对象。IPython 已经完全替换了这个对象,以便首先将标准输出发送到笔记本(见下文)。
Python 3 的前景会更好吗,因为可以将另一层潜入 IPython 内核中的 print() 堆栈中?尤其是对于那些没有通过 Python 链接到达这里的人,
不-覆盖 sys.stdout 是您所需要的,而不是打印本身(见上文、下文和其他地方)。Python 3 在这里没有任何优势。
[没有人期待西班牙宗教裁判所] 更一般地说,您能否指出(与语言无关的)多个流被传送到一个页面的示例?
当然——IPython 笔记本本身。它使用消息 ID 和元数据来确定标准输出消息的来源,以及这些消息应该在哪里结束。下面,在我对一个显然没有人问过的问题的原始答案中,我展示了一个同时绘制来自多个线程同时运行的单元的输出的示例。
为了获得您想要的刷新行为,您可能需要做两件事:
- 将 sys.stdout 替换为您自己的对象,该对象使用 IPython 显示协议发送带有您自己的线程识别元数据(例如
threading.current_thread().ident
)的消息。这应该在上下文管理器中完成(如下所示),因此它只会影响您实际想要的打印语句。
- 编写一个 IPython js 插件来处理您的新格式的标准输出消息,以便它们不会立即绘制,而是存储在数组中,等待绘制。
原始答案(错误但相关的问题):
它依赖于一些恶作剧和私有 API,但这对于当前的 IPython 来说是完全可能的(它可能不会永远存在)。
这是一个示例笔记本: http: //nbviewer.ipython.org/4563193
为了做到这一点,您首先需要了解 IPython 如何将标准输出发送到笔记本。这是通过将 sys.stdout 替换为OutStream对象来完成的。这缓冲数据,然后在sys.stdout.flush
被调用时通过 zeromq 发送,最终在浏览器中结束。
现在,如何将输出发送到特定单元格。
IPython消息协议
使用“父”标头来识别哪个请求产生了哪个回复。每次您要求 IPython 运行一些代码时,它都会设置各种对象(包括 sys.stdout)的父标头,以便它们的副作用消息与导致它们的消息相关联。当您在线程中运行代码时,这意味着当前的 parent_header 只是最近的 execute_request,而不是启动任何给定线程的原始请求。
考虑到这一点,这里有一个上下文管理器,它临时将 stdout 的父标头设置为特定值:
import sys
from contextlib import contextmanager
stdout_lock = threading.Lock()
@contextmanager
def set_stdout_parent(parent):
"""a context manager for setting a particular parent for sys.stdout
the parent determines the destination cell of output
"""
save_parent = sys.stdout.parent_header
# we need a lock, so that other threads don't snatch control
# while we have set a temporary parent
with stdout_lock:
sys.stdout.parent_header = parent
try:
yield
finally:
# the flush is important, because that's when the parent_header actually has its effect
sys.stdout.flush()
sys.stdout.parent_header = save_parent
这是一个 Thread ,它在线程启动时记录父级,并在每次发出 print 语句时应用该父级,因此它的行为就好像它仍在原始单元格中一样:
import threading
class counterThread(threading.Thread):
def run(self):
# record the parent when the thread starts
thread_parent = sys.stdout.parent_header
for i in range(3):
time.sleep(2)
# then ensure that the parent is the same as when the thread started
# every time we print
with set_stdout_parent(thread_parent):
print i
最后,一个笔记本将它们捆绑在一起,时间戳显示实际并发打印到多个单元格:
http://nbviewer.ipython.org/4563193/