web-dev-qa-db-ja.com

出力バッファリングを無効にする

Pythonのsys.stdoutのインタプリタでは、出力バッファリングはデフォルトで有効になっていますか?

答えが肯定的であれば、それを無効にするためのすべての方法は何ですか?

これまでの提案:

  1. -uコマンドラインスイッチを使う
  2. 書き込みごとにフラッシュするオブジェクトでsys.stdoutをラップします。
  3. PYTHONUNBUFFERED env varを設定します
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

実行中にプログラムでsys/sys.stdoutにグローバルフラグを設定する他の方法はありますか?

457
Eli Bendersky

Magnus Lyckaからのメーリングリストでの回答

"python -u"(または#!/ usr/bin/env python -uなど)を使用するか、環境変数PYTHONUNBUFFEREDを設定することで、pythonプロセス全体のバッファリングをスキップできます。

Sys.stdoutを、呼び出しのたびにフラッシュするラッパーのような他のストリームに置き換えることもできます。

class Unbuffered(object):
   def __init__(self, stream):
       self.stream = stream
   def write(self, data):
       self.stream.write(data)
       self.stream.flush()
   def writelines(self, datas):
       self.stream.writelines(datas)
       self.stream.flush()
   def __getattr__(self, attr):
       return getattr(self.stream, attr)

import sys
sys.stdout = Unbuffered(sys.stdout)
print 'Hello'
382
Seb

私の答えを Python printの出力をフラッシュするにはどうすればいいですか? または Pythonのprint関数をフラッシュします。それが呼ばれたときにバッファ? が、それらはこのものの複製としてマークされていたので(私は賛成できません)、ここでそれに答えます。

Python 3.3のprint()はキーワード引数 "flush"をサポートしています( ドキュメント を参照):

print('Hello World!', flush=True)
86
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

クレジット: "Sebastian"、Pythonメーリングリストのどこかにあります。

第三者による編集

最近のバージョンのPython 3ではサポートされていません

72

はい、そうです。

コマンドラインで "-u"スイッチを使って無効にすることができます。

あるいは、書き込みのたびにsys.stdoutで.flush()を呼び出すこともできます(またはこれを自動的に行うオブジェクトでラップすることもできます)。

46
Brian
def disable_stdout_buffering():
    # Appending to gc.garbage is a way to stop an object from being
    # destroyed.  If the old sys.stdout is ever collected, it will
    # close() stdout, which is not good.
    gc.garbage.append(sys.stdout)
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

# Then this will give output in the correct order:
disable_stdout_buffering()
print "hello"
subprocess.call(["echo", "bye"])

古いsys.stdoutを保存しないと、disable_stdout_buffering()は冪等ではなく、複数回呼び出すと次のようなエラーになります。

Traceback (most recent call last):
  File "test/buffering.py", line 17, in <module>
    print "hello"
IOError: [Errno 9] Bad file descriptor
close failed: [Errno 9] Bad file descriptor

他の可能性は:

def disable_stdout_buffering():
    fileno = sys.stdout.fileno()
    temp_fd = os.dup(fileno)
    sys.stdout.close()
    os.dup2(temp_fd, fileno)
    os.close(temp_fd)
    sys.stdout = os.fdopen(fileno, "w", 0)

(gc.garbageに追加するのは、それほど不自然なサイクルが発生するところであり、それらをチェックしたいと思うかもしれないので、それほど良い考えではありません。)

13
Mark Seaborn

これはCristóvãoD. Sousaの答えに関連していますが、私はまだコメントできませんでした。

常にバッファリングされていない出力を得るためにPython 3flushキーワード引数を使用する簡単な方法は次のとおりです。

import functools
print = functools.partial(print, flush=True)

その後、printは常に出力を直接フラッシュします(flush=Falseが指定されている場合を除く)。

(a)これはすべての出力をリダイレクトするわけではないので、これは部分的にしか質問に答えないことに注意してください。しかし、私はprintがpythonでstdout/stderrへの出力を作成するための最も一般的な方法であると思うので、これらの2行はおそらくほとんどのユースケースをカバーします。

それがあなたがそれを定義したモジュール/スクリプトでだけ働くことに注意してください(b)。 sys.stdoutをめちゃくちゃにしないので、モジュールを書くときこれは良いことです。

Python 2flush引数を提供していませんが、ここで説明されているようにPython 3タイプのprint関数をエミュレートすることができます https://stackoverflow.com/a/27991478/3734258

12
ciem

以下はPython 2.6、2.7、および3.2で動作します。

import os
import sys
buf_arg = 0
if sys.version_info[0] == 3:
    os.environ['PYTHONUNBUFFERED'] = '1'
    buf_arg = 1
sys.stdout = os.fdopen(sys.stdout.fileno(), 'a+', buf_arg)
sys.stderr = os.fdopen(sys.stderr.fileno(), 'a+', buf_arg)
12
Gummbum

はい、デフォルトで有効になっています。 pythonを呼び出すときにコマンドラインで-uオプションを使用すると無効にできます。

10
Nathan

Pythonを stdbuf ユーティリティで実行することもできます。

stdbuf -oL python <script>

7
dyomas

(私はコメントを投稿しましたが、それはどういうわけか紛失しました。それで、また:)

  1. 私が気づいたように、(少なくともLinux上で)CPythonは出力がどこに行くかによって異なった振る舞いをします。それがttyになると、出力はそれぞれの\n'の後にフラッシュされます
    もしそれがパイプ/プロセスに行くなら、それはバッファされていて、あなたはflush()ベースの解決法か上で推薦された - uオプションを使うことができます。

  2. 出力バッファリングと多少関係があります。
    入力の行を次のように繰り返すと

    for line in sys.stdin:
    ...

次に、CPythonfor実装は、しばらくの間入力を収集してから、一連の入力行に対してループ本体を実行します。スクリプトが各入力行に出力を書き込もうとしている場合、これは出力バッファリングのように見えるかもしれませんが、実際にはバッチ処理です。したがって、flush()などの手法は役に立ちません。興味深いことに、pypyではこのような振る舞いはありません。これを避けるためには、

while True: line=sys.stdin.readline()
...

3
tzp

sys.stdoutwriteメソッドのみをflushを呼び出すものでオーバーライドすることが可能です。推奨されるメソッドの実装は以下の通りです。

def write_flush(args, w=stdout.write):
    w(args)
    stdout.flush()

w引数のデフォルト値は、元のwriteメソッド参照を保持します。 write_flushが定義された後、元のwriteがオーバーライドされる可能性があります。

stdout.write = write_flush

コードはstdoutfrom sys import stdoutのようにインポートされていることを前提としています。

3
Vasily E.

Fcntlを使用してファイルのフラグをその場で変更することもできます。

fl = fcntl.fcntl(fd.fileno(), fcntl.F_GETFL)
fl |= os.O_SYNC # or os.O_DSYNC (if you don't care the file timestamp updates)
fcntl.fcntl(fd.fileno(), fcntl.F_SETFL, fl)
3
jimx

クラッシュすることなく(少なくともwin32; python 2.7、ipython 0.12で)動作し、その後(複数回)呼ばれるという変形です。

def DisOutBuffering():
    if sys.stdout.name == '<stdout>':
        sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    if sys.stderr.name == '<stderr>':
        sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
3
Laimis

バッファなしファイルを作成し、このファイルをsys.stdoutに割り当てることができます。

import sys 
myFile= open( "a.log", "w", 0 ) 
sys.stdout= myFile

システム提供の標準出力を魔法のように変更することはできません。それはOSによってあなたのPythonプログラムに供給されるからです。

2
S.Lott

Python 3では、print関数をmonkey-patchして、常にflush = Trueを送ることができます。

_orig_print = print

def print(*args, **kwargs):
    _orig_print(*args, flush=True, **kwargs)
1
Oliver

バッファリングされていない出力を取得する1つの方法は、sys.stderrの代わりにsys.stdoutを使用するか、単にsys.stdout.flush()を呼び出して明示的に書き込みを強制することです。

次のようにすれば、印刷されたものすべてを簡単にリダイレクトできます。

import sys; sys.stdout = sys.stderr
print "Hello World!"

あるいは特定のprintステートメントだけをリダイレクトするには:

print >>sys.stderr, "Hello World!"

標準出力をリセットするには、次のようにします。

sys.stdout = sys.__stdout__
1
stderr