9

私はこれを問題として提起したくありません。なぜなら、それはかなり素晴らしいツールであるものに対する完全に不合理な機能要求のように思われるからです。しかし、読者がアーキテクチャに精通している場合は、潜在的な拡張が実現可能かどうかを知りたいと思います。

私は最近、実行したときに何が起こるかを確認するために、いくつかの単純なスレッドコードを含むノートブックを作成しました。ノートブックコード(tl; drは、スリープループで出力する多数の並列スレッドを開始します)は、https://gist.github.com/4562840で入手できます。

コードの実行中にSHIFT-RETURNを数回押すと、コードが実行されたセルの出力領域ではなく、現在のセルの出力領域にカーネルからの出力が表示されることがわかります。

セルに対してスレッドがアクティブである場合、出力領域を非同期で更新できる「更新」ボタンを表示できるかどうか疑問に思いました。理想的には、すべてのスレッドが終了した後(最終更新後)にクリックすると、更新ボタンが消えます。

ただし、これは、各スレッドの印刷出力を識別してインターセプトし、特定のセルの出力用のバッファーに転送できるかどうかに依存します。それで、2つの質問。

  1. Python 2のprintステートメントのハードワイヤリングは、この拡張機能を標準のインタープリターでは実装できないことを意味すると信じていますか?

  2. IPythonカーネル内のprint()スタックに別のレイヤーを忍び込ませることができることを考えると、Python 3の見通しはさらに良くなりますか?特に、Pythonリンクをたどってここに到達しなかった人にとっては、

  3. [スペイン異端審問を誰も期待していません]より一般的には、ページに配信される複数のストリームの(言語に依存しない)例を指摘できますか?これを処理するためにDOMを構築および変更するための確立されたベストプラクティスはありますか?

4

1 に答える 1

14

アップデート:

Python 2 の print ステートメントがハードワイヤリングされているということは、この拡張機能を標準のインタープリターで実装できないことを意味していると私は信じていますか?

いいえ、print ステートメントの重要な部分はまったく組み込まれていません。writeprint は単に sys.stdout に書き込みます。これはおよびflushメソッドを持つ任意のオブジェクトにすることができます。IPython は、最初に stdout をノートブックに取得するために、このオブジェクトを既に完全に置き換えています (以下を参照)。

IPython カーネル内の print() スタックに別のレイヤーを忍び込ませることができることを考えると、Python 3 の見通しはさらに良くなりますか?特に Python リンクをたどってここにたどり着かなかった人にとっては、

いいえ - 必要なのは sys.stdout をオーバーライドすることだけであり、それ自体を印刷する必要はありません (上記、以下、および他の場所を参照)。ここには Python 3 の利点はありません。

[誰もスペインの異端審問を期待していない] もっと一般的に言えば、複数のストリームが 1 つのページに配信される (言語にとらわれない) 例を挙げていただけますか?

確かに - IPython ノートブック自体。メッセージ ID とメタデータを使用して、stdout メッセージの発信元と、これらのメッセージの最終的な送信先を決定します。以下に、明らかに誰も尋ねなかった質問に対する最初の回答で、スレッドが同時に実行されている複数のセルからの出力を同時に描画する例を示します。

必要な更新動作を得るには、おそらく次の 2 つのことを行う必要があります。

  1. sys.stdout を、IPython 表示プロトコルを使用して独自のスレッド識別メタデータを含むメッセージを送信する独自​​のオブジェクトに置き換えます (例: threading.current_thread().ident)。これは、コンテキスト マネージャー (以下のように) で行う必要があるため、実際に必要な print ステートメントにのみ影響します。
  2. 標準出力メッセージの新しい形式を処理するための IPython js プラグインを作成して、すぐに描画されるのではなく、描画されるのを待って配列に格納されるようにします。

元の回答(間違っていますが、関連する質問):

いくつかの悪ふざけとプライベート API に依存していますが、これは現在の IPython で完全に可能です (それは永遠ではないかもしれません)。

ノートブックの例を次に示します: http://nbviewer.ipython.org/4563193

これを行うには、最初に IPython がノートブックに標準出力を取得する方法を理解する必要があります。これは、sys.stdout をOutStreamオブジェクトに置き換えることによって行われます。これはデータをバッファリングし、呼び出されたときに zeromq を介して送信し、sys.stdout.flush最終的にブラウザで終了します。

次に、特定のセルに出力を送信する方法について説明します。

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

そして、スレッドの開始時に親を記録し、print ステートメントを作成するたびにその親を適用する Thread は、元のセルにまだあるかのように動作します。

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/

于 2013-01-18T08:54:49.687 に答える