将sys.stdout重定向到特定的Jupyter Notebook单元格

33

Jupyter==4.1.0,Python==2.7.10,IPython==4.2.0

我正在为我的Jupyter笔记本编写SQL UI,并希望使用多线程,以便在运行查询时可以在其他单元格中继续工作。

问题是,如果我在一个单元格中执行查询,则输出将显示在最后执行的单元格的输出提示中,而不是在执行查询的单元格的输出提示中。

我搜索了互联网并发现了这个聪明的技巧,但我认为它已过时和/或不再适用于我的Jupyter版本。当我运行它时,我只得到最后执行的单元格的输出。因此,如果我同时运行两个单元格,我只会得到最后执行的输出,而不是同时打印到不同单元格的输出。

所以我有一个上下文管理器,它设置了parent_header

import sys
import threading
from contextlib import contextmanager

# we need a lock so that other threads don't snatch control
# while we have set a temporary parent
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 the output
    """
    save_parent = sys.stdout.parent_header
    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

我希望能够获取单元格 In[1] 的 parent_header,并将单元格 In[2] 的输出重定向到 In[1] 的输出。

例如:

获取 In[1] 的 parent_header

In[1]: t = sys.stdout.parent_header

接下来的代码将会运行,但是输出应该打印到Out[1](目前运行此代码时没有输出):
In [2]: with set_stdout_parent(t):
            print 'FOO'

这应该会产生:

In[1]: t = sys.stdout.parent_header
Out[1]:'FOO'

我的次优解决方案是在其自己的线程上运行查询并打印一个带有唯一ID的HTML容器,然后当查询完成时,使用IPython的displayJavascript函数将其附加到适当的DOM对象。 - tmthyjames
4
把输出保存到一个文件中,然后在另一个单元格中打开并打印该文件怎么样? - Konstantino Sparakis
1
你尝试过像这样的方法吗? - Jorden
这个怎么样:ipykernel.iostream.OutStream - Michael H
2个回答

1

关于ipywidgets.Output的文档中有一节关于从后台线程与输出小部件交互的内容。使用Output.append_stdout方法时,无需锁定。然后可以使用此答案中的最终单元格替换。

def t1_main():
    for i in range(10):
        output1.append_stdout(f'thread1 {i}\n')
        time.sleep(0.5)


def t2_main():
    for i in range(10):
        output2.append_stdout(f'thread2 {i}\n')
        time.sleep(0.5)

output1.clear_output()
output2.clear_output()
        
t1 = Thread(target=t1_main)
t2 = Thread(target=t2_main)
t1.start()
t2.start()
t1.join()
t2.join()

0

您可以使用 ipywidgets.Output (docs) 和锁定的组合:

enter image description here

在Jupyter单元格中编写代码:

# In[1]:


from threading import Thread, Lock
import time
from ipywidgets import Output


# In[2]:


output1 = Output()
output1


# In[3]:


output2 = Output()
output2


# In[4]:


print_lock = Lock()
def t1_main():    
    for i in range(10):
        with print_lock, output1:
            print('thread1', i)
        time.sleep(0.5)

def t2_main():
    for i in range(10):
        with print_lock, output2:
            print('thread2', i)
        time.sleep(0.5)

output1.clear_output()
output2.clear_output()
        
t1 = Thread(target=t1_main)
t2 = Thread(target=t2_main)
t1.start()
t2.start()
t1.join()
t2.join()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接