Python=でtqdm
を使用して、スクリプトにコンソールプログレスバーを表示しています。ただし、コンソールにprint
メッセージを送信する関数を呼び出す必要があります。一般的に、コンソールにプログレスバーを表示しながらコンソールに書き込むと、次のように表示が乱れます。
_from time import sleep
from tqdm import tqdm
def blabla():
print "Foo blabla"
for k in tqdm(range(3)):
blabla()
sleep(.5)
_
これにより出力が作成されます。
_0%| | 0/3 [00:00<?, ?it/s]Foo
blabla
33%|###########6 | 1/3 [00:00<00:01, 2.00it/s]Foo
blabla
67%|#######################3 | 2/3 [00:01<00:00, 2.00it/s]Foo
blabla
100%|###################################| 3/3 [00:01<00:00, 2.00it/s]
_
tqdm
のドキュメントによると、メソッドtqdm.write()
は、表示されたプログレスバーを壊さずにコンソールにメッセージを書き込む手段を提供します。したがって、正しい出力は次のスニペットによって提供されます。
_from time import sleep
from tqdm import tqdm
def blabla():
tqdm.write("Foo blabla")
for k in tqdm(range(3)):
blabla()
sleep(.5)
_
そして、このようになります:
_Foo blabla
Foo blabla
Foo blabla
100%|###################################| 3/3 [00:01<00:00, 1.99it/s]
_
一方、この ソリューションは、_sys.stdout
_をvoidに非常にエレガントにリダイレクトすることで、これらの関数 を沈黙させることができます。これは、機能を無音にするのに最適です。
それでもプログレスバーを壊さずにこれらの関数からのメッセージを表示したいので、_sys.stdout
_をtqdm.write()
にリダイレクトし、次にtqdm.write()
old_sys.stdout
_に書き込みます。これはスニペットになります:
_from time import sleep
import contextlib
import sys
from tqdm import tqdm
class DummyFile(object):
file = None
def __init__(self, file):
self.file = file
def write(self, x):
tqdm.write(x, file=self.file)
@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = DummyFile(save_stdout)
yield
sys.stdout = save_stdout
def blabla():
print "Foo blabla"
for k in tqdm(range(3)):
with nostdout():
blabla()
sleep(.5)
_
ただし、これにより、以前のように実際にはさらにめちゃくちゃな出力が作成されます。
_0%| | 0/3 [00:00<?, ?it/s]Foo
blabla
33%|###########6 | 1/3 [00:00<00:01, 2.00it/s]Foo
blabla
67%|#######################3 | 2/3 [00:01<00:00, 2.00it/s]Foo
blabla
100%|###################################| 3/3 [00:01<00:00, 2.00it/s]
_
参考:tqdm.write(..., end="")
の内部でDummyFile.write()
を呼び出すと、まだめちゃくちゃになっている最初の出力と同じ結果が作成されます。
tqdm.write()
は、メッセージを書き込む前にプログレスバーをクリアしてから、プログレスバーを書き直すため、これが機能しない理由を理解できません。
何が欠けていますか?
_sys.stdout
_のリダイレクトは常にトリッキーであり、2つのアプリケーションが同時にそれをいじるときに悪夢になります。
ここでのトリックは、tqdm
がデフォルトで_sys.stderr
_ではなく_sys.stdout
_に出力することです。通常、tqdm
には、これら2つの特別なチャネルに対するアンチミックスアップ戦略がありますが、_sys.stdout
_をリダイレクトしているため、ファイルハンドルが変更されるため、tqdm
は混乱します。
したがって、_file=sys.stdout
_をtqdm
に明示的に指定するだけで機能します。
_from time import sleep
import contextlib
import sys
from tqdm import tqdm
class DummyFile(object):
file = None
def __init__(self, file):
self.file = file
def write(self, x):
# Avoid print() second call (useless \n)
if len(x.rstrip()) > 0:
tqdm.write(x, file=self.file)
@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = DummyFile(sys.stdout)
yield
sys.stdout = save_stdout
def blabla():
print("Foo blabla")
# tqdm call to sys.stdout must be done BEFORE stdout redirection
# and you need to specify sys.stdout, not sys.stderr (default)
for _ in tqdm(range(3), file=sys.stdout):
with nostdout():
blabla()
sleep(.5)
print('Done!')
_
また、出力をより良くするためにいくつかのトリックを追加しました(たとえば、_\n
_なしでprint()
を使用する場合、役に立たない_end=''
_はありません)。
/ EDIT:実際には、stdout
を開始した後でtqdm
リダイレクトを実行できるようです。tqdm
で_dynamic_ncols=True
_を指定するだけです。
悪いかもしれませんが、内蔵の印刷機能を変更しました。
import inspect
import tqdm
# store builtin print
old_print = print
def new_print(*args, **kwargs):
# if tqdm.tqdm.write raises error, use builtin print
try:
tqdm.tqdm.write(*args, **kwargs)
except:
old_print(*args, ** kwargs)
# globaly replace print with new_print
inspect.builtins.print = new_print
User493630と複雑な答えを組み合わせて、tqdm
のfile=sys.stdout
パラメータを使用する必要がないようにするこのコンテキストマネージャを作成しました。
import inspect
import contextlib
import tqdm
@contextlib.contextmanager
def redirect_to_tqdm():
# Store builtin print
old_print = print
def new_print(*args, **kwargs):
# If tqdm.tqdm.write raises error, use builtin print
try:
tqdm.tqdm.write(*args, **kwargs)
except:
old_print(*args, ** kwargs)
try:
# Globaly replace print with new_print
inspect.builtins.print = new_print
yield
finally:
inspect.builtins.print = old_print
それを使用するには、単に:
for i in tqdm.tqdm(range(100)):
with redirect_to_tqdm():
time.sleep(.1)
print(i)
さらに簡単にするために、コードを新しい関数でラップすることができます:
def tqdm_redirect(*args, **kwargs):
with redirect_to_tqdm():
for x in tqdm.tqdm(*args, **kwargs):
yield x
for i in tqdm_redirect(range(20)):
time.sleep(.1)
print(i)
OPのソリューションはほぼ正しいです。あなたの出力を台無しにするtqdmライブラリのテストはこれです( https://github.com/tqdm/tqdm/blob/master/tqdm/_tqdm.py#L546-L549 ):
_if hasattr(inst, "start_t") and (inst.fp == fp or all(
f in (sys.stdout, sys.stderr) for f in (fp, inst.
inst.clear(nolock=True)
inst_cleared.append(inst)
_
_tqdm.write
_は、提供するファイルをテストして、印刷するテキストと潜在的なtqdmバーの間に衝突のリスクがないかどうかを確認しています。あなたの場合、stdoutとstderrがターミナルで混合されるので、衝突があります。これに対抗するため、テストに合格すると、tqdmはバーをクリアし、テキストを印刷して、後でバーを描画します。
ここで、テスト_fp == sys.stdout
_は、_sys.stdout
_がDummyFile
になり、fpが実際の_sys.stdout
_であるために失敗し、クリーニング動作は有効になっていません。 DummyFile
の単純な等価演算子はすべてを修正します。
_class DummyFile(object):
def __init__(self, file):
self.file = file
def write(self, x):
tqdm.write(x, end="", file=self.file)
def __eq__(self, other):
return other is self.file
_
また、printは改行を_sys.stdout
_に渡すため(またはユーザーの選択に依存しないため)、tqdmが独自に別の行を追加することを望まないため、オプション_end=''
_よりもコンテンツに対してstrip
を実行します。
Gaborousの答えにより、tqdm(..., file=sys.stdout)
は出力ストリームを小節で汚染します。 _file=sys.stdout
_(デフォルト)を維持することにより、ストリームを分離したままにできます。
Conchylicultorとuser493630の回答を使用すると、パッチ印刷のみを行うことができます。ただし、ロギングなどの他のシステムはsys.stdoutに直接ストリーミングするため、_tqdm.write
_を経由しません。