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


532

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

答えが肯定的な場合、それを無効にするすべての方法は何ですか?

これまでの提案:

  1. -uコマンドラインスイッチを使用する
  2. sys.stdoutすべての書き込み後にフラッシュするオブジェクトでラップする
  3. PYTHONUNBUFFERED環境変数を設定
  4. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

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


7
Python 3の「印刷」については、この回答を参照してください。
Antti Haapala 2016年

1
の欠点は-u、コンパイルされたバイトコードや、__main__.pyエントリポイントとしてファイルを使用するアプリでは機能しないことです。
akhan

フルCPythonの初期化ロジックはここにある:github.com/python/cpython/blob/v3.8.2/Python/...
紅Cherniavsky-Paskin

回答:


443

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'

71
元のsys.stdoutは、引き続きsys .__ stdout__として入手できます。必要な場合に備えて=)
Antti Rasinen '09 / 09/20

40
#!/usr/bin/env python -u動かない!! 参照ここで
WIM

6
__getattr__継承を避けるためだけに!?
Vladimir Keleshev 2013

32
いくつかの頭痛を省くためのいくつかのメモ:気づいたように、出力がttyに送られるか、別のプロセス/パイプに送られるかによって、出力バッファリングの動作は異なります。それがttyに行く場合、それは各\ nの後にフラッシュされますが、パイプではバッファリングされます。後者の場合、これらの洗浄液を使用できます。Cpythonの場合(pypyではなく!!!):sys.stdin: ...のfor行で入力を反復すると、forループは、ループの本体が実行される前に多数の行を収集します。これはバッファリングのように動作しますが、バッチ処理です。代わりに、trueの間
tzp

5
@tzp:ループのiter()代わりに使用できwhileます:for line in iter(pipe.readline, ''):for line in pipe:できるだけ早く生成されるPython 3では必要ありません。
jfs

122

私の答えを印刷関数の出力をフラッシュする方法に入れたいですか?またはそれが呼び出されたときにバッファをフラッシュするPythonのprint関数では?、しかし、それらはこの1つ(私が同意しないもの)の複製としてマークされたので、ここで答えます。

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

print('Hello World!', flush=True)

77
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
import io, os, sys
try:
    # Python 3, open as binary, then wrap in a TextIOWrapper with write-through.
    sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True)
    # If flushing on newlines is sufficient, as of 3.7 you can instead just call:
    # sys.stdout.reconfigure(line_buffering=True)
except TypeError:
    # Python 2
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

クレジット:Pythonメーリングリストのどこかにある「セバスチャン」。


Python3では、print関数の名前をフラッシュする名前でオーバーライドできます。その汚いトリックも!
meawoppl 2014年

16
@meawoppl:Python 3.3以降flush=Trueprint()関数にパラメーターを渡すことができました。
jfs

応答を表示するための応答の編集は、最新バージョンのPythonでは無効です
Mike

両方os.fdopen(sys.stdout.fileno(), 'wb', 0)bバイナリの場合は注意)とflush=True3.6.4で動作します。ただし、Pythonの複数のインスタンスがインストールされている場合は、サブプロセスを使用して別のスクリプトを開始する場合は、を指定したことを確認してくださいpython3
not2qubit 2018

1
@ not2qubit:使用するos.fdopen(sys.stdout.fileno(), 'wb', 0)場合、TextIOストリームではなくバイナリファイルオブジェクトになります。TextIOWrapperミックスにaを追加する必要があります(write_throughすべてのバッファーを削除できるようにするかline_buffering=True、改行のみをフラッシュするために使用するようにします)。
Martijn Pieters

55

はい、そうです。

コマンドラインで「-u」スイッチを使用して無効にできます。

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


19

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

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

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

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

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

(b)定義したモジュール/スクリプトでのみ機能することに注意してください。これは、モジュールを混乱させないため、モジュールを作成するときに役立ちsys.stdoutます。

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


1
flushpython2にはkwarg がないことを除いて。
o11c

@ o11c、そうだね。私は(私はそれをテストしたが、どういうわけか、私は一見混乱していたと確信していた:私は今の罰金を願っおかげで、私の答えを変更しました。!
ティム

14
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に追加することは、解放不可能なサイクルが発生する場所であり、それらを確認したい場合があるため、あまり良い考えではありません。)


2
一部の人が示唆stdoutするsys.__stdout__ように古いものがまだ生きているなら、ゴミのことは必要ないでしょうね?しかし、それはクールなトリックです。
Thomas Ahle 14

1
@Federicoの回答と同様に、これはPython 3では機能しません。をValueError: can't have unbuffered text I/O呼び出すと例外がスローされるためprint()です。
gbmhunter 2018

あなたの「別の可能性」は最初は最も堅牢なソリューションのように見えますが、残念ながら、sys.stdout.close()の後でos.dup2(temp_fd、filenoの前に別のスレッドがopen()を呼び出す場合、競合状態になります)。ThreadSanitizerであなたのテクニックを試してみたところ、このことがわかりました。このようなopen()との競合時にdup2()がEBUSYで失敗するという事実により、失敗はさらに大きくなります。参照stackoverflow.com/questions/23440216/...
ドンハッチ

13

以下は、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)

これを2回実行すると、Windowsでクラッシュします:-)
Michael Clerx

@MichaelClerxうーんうーん、いつもファイルxDを閉じることを忘れないでください。

Raspbian 9のPython 3.5 OSError: [Errno 29] Illegal seekは、次の行を提供してくれますsys.stdout = os.fdopen(sys.stdout.fileno(), 'a+', buf_arg)
sdbbs

12

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


7

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

stdbuf -oL python <script>


2
ラインバッファリング(-oL有効化)はまだバッファリングされています-f / estackoverflow.com/questions/58416853/…を参照して、end=''出力がすぐに表示されなくなる理由を尋ねてください。
Charles Duffy、

確かに、しかし、ラインバッファリング(TTY付き)デフォルトであるので、それは出力を想定し、書き込みコードに意味を持たず、完全にバッファリングされていない-多分よりよい明示的にprint(..., end='', flush=True)どこのimprotantこと?OTOHでは、複数のプログラムが同時に同じ出力に書き込む場合、トレードオフはすぐに進行するのではなく、出力の取り違えを減らすことにシフトする傾向があり、ラインバッファリングが魅力的になります。それで、明示的に書いてバッファリングを外部で制御しない方良いのではないflushでしょうか?
Beni Cherniavsky-Paskin

私は違うと思います。プロセス自体が、いつ、なぜそれを呼び出すかを決定する必要がありますflush。外部バッファリング制御は、ここでの回避策として強制されます
dyoma

7

Python 3では、print関数をモンキーパッチして、常にflush = Trueを送信できます。

_orig_print = print

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

コメントで指摘されているように、フラッシュパラメータを値にバインドすることで、これを簡略化できますfunctools.partial

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

3
ただ疑問に思っていますが、それは完璧なユースケースではfunctools.partialないでしょうか?
0xC0000022L

@ 0xC0000022Lに感謝します。これにより見栄えが良くなります!print = functools.partial(print, flush=True)私にとってはうまくいきます。
MarSoft、

@ 0xC0000022L確かに、私はそのオプションを示すために投稿を更新しました。それを指摘してくれてありがとう
Oliver

3
それをどこにでも適用したい場合import builtins; builtins.print = partial(print, flush=True)
Perkins

4

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)

1
同等の窓があります:stackoverflow.com/questions/881696/...
東武

12
O_SYNCは、ユーザー空間レベルのバッファリングとは何の関係もありません。
apenwarr 2012

4

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

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

w引数のデフォルト値は、元のwriteメソッド参照を保持します。が定義された write_flush、オリジナルwriteが上書きされる可能性があります。

stdout.write = write_flush

コードは、stdoutこの方法でインポートされることを前提としていfrom sys import stdoutます。


3

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

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

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


3

クラッシュせずに動作するバリアント(少なくとも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)

これはバッファリングされていませんか?
クォンタム

1
sys.stdout is sys.__stdout__name属性を持つ置換オブジェクトに依存する代わりに、チェックする必要がありますか?
leewz 2014年

これは、gunicornが何らかの理由でPYTHONUNBUFFEREDを尊重していない場合に最適です。
Brian Arsuaga、2015

3

(コメントを投稿しましたが、どういうわけか失われました。そのため、もう一度:)

  1. 気づいたように、CPython(少なくともLinuxでは)の動作は、出力先によって異なります。ttyに行く場合、出力は毎回フラッシュされます ' \n'
    パイプ/プロセスに行く場合、それはバッファされ、flush()ベースのソリューションまたは上記の-uオプションを使用できます。

  2. 出力のバッファリングにわずかに関連:
    入力の行を反復する場合

    for line in sys.stdin:
    ...

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

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


こちらがコメントです。古いバージョンのPythonのバグかもしれません。サンプルコードを提供できますか?for line in sys.stdinvs.のようなものfor line in iter(sys.stdin.readline, "")
-jfs

sys.stdinの行の場合:print( "Line:" + line); sys.stdout.flush()
tzp 2013年

それは次のようになります先読みバグ。これは、Python 2で、stdinがパイプの場合にのみ発生します。私の以前のコメントのコードは、問題を示していfor line in sys.stdinます(応答が遅れています)
jfs

2

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

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

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

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

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

stdoutをリセットするには、次のようにします。

sys.stdout = sys.__stdout__

1
後で標準のリダイレクトを使用して出力をキャプチャしようとすると、これは非常に混乱する可能性があり、何もキャプチャしていないことがわかります。psあなたの標準出力は太字になっています。
空き容量

1
stderrに選択的に出力することについての1つの大きな注意は、これにより行がずれて表示されるため、タイムスタンプもない限り、これは非常に混乱する可能性があります。
haridsv 2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.