コンテンツにスキップ

プログレスバー

時間のかかる操作を実行する場合は、ユーザーに知らせることができます。🤓

プログレスバー

Richのプログレス表示を使用して、プログレスバーを表示できます。例えば

import time

import typer
from rich.progress import track


def main():
    total = 0
    for value in track(range(100), description="Processing..."):
        # Fake processing time
        time.sleep(0.01)
        total += 1
    print(f"Processed {total} things.")


if __name__ == "__main__":
    typer.run(main)

反復処理したいものをRichの`track()`の中に配置し、それを反復処理します。

確認してください

$ python main.py

---> 100%

Processed 100 things.

...実際には、ずっときれいに見えます。✨しかし、ドキュメントではアニメーションを表示できません。😅

色と情報は次のようになります

$ python main.py

Processing... <font color="#F92672">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸</font><font color="#3A3A3A">━━━━━━━━━━</font> <font color="#AE81FF"> 74%</font> <font color="#A1EFE4">0:00:01</font>

スピナー

操作にどれくらいの時間がかかるかわからない場合は、代わりにスピナーを使用できます。

Richを使用すると、複雑で高度な方法で多くのものを表示できます。

たとえば、これは2つのスピナーを表示します

import time

import typer
from rich.progress import Progress, SpinnerColumn, TextColumn


def main():
    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        transient=True,
    ) as progress:
        progress.add_task(description="Processing...", total=None)
        progress.add_task(description="Preparing...", total=None)
        time.sleep(5)
    print("Done!")


if __name__ == "__main__":
    typer.run(main)

ドキュメントでは美しいアニメーションを表示できません。😅

しかし、ある時点で、これは次のようになります(回転していると想像してください)。🤓

$ python main.py

<font color="#A6E22E">⠹</font> Processing...
<font color="#A6E22E">⠹</font> Preparing...

詳しくは、プログレス表示に関するRichのドキュメントをご覧ください。

Typer `progressbar`

可能であれば、上記のように**Rich**を使用することをお勧めします.より多くの機能があり、より高度で、情報をより美しく表示できます。✨

ヒント

Richを使用できる場合は、上記のRichのドキュメントを使用し、このページの残りはスキップしてください。😎

ただし、Richを使用できない場合、Typer(実際にはClick)にはプログレスバーを表示するためのシンプルなユーティリティが付属しています。

情報

`typer.progressbar()`はClickから直接提供されています。Clickのドキュメントで詳細を読むことができます。

`typer.progressbar`を使用する

ヒント

このためには、Richを使用する方がはるかに優れていることを忘れないでください。😎

`typer.progressbar()`は、次のように`with`ステートメントで使用できます。

with typer.progressbar(something) as progress:
    pass

そして、`typer.progressbar()`に関数引数として、通常反復処理するものを渡します。

import time

import typer


def main():
    total = 0
    with typer.progressbar(range(100)) as progress:
        for value in progress:
            # Fake processing time
            time.sleep(0.01)
            total += 1
    print(f"Processed {total} things.")


if __name__ == "__main__":
    typer.run(main)

したがって、ユーザーのリストがある場合、これは次のようになります。

users = ["Camila", "Rick", "Morty"]

with typer.progressbar(users) as progress:
    pass

`typer.progressbar()`を使用した`with`ステートメントは、通常反復処理するものと同じように反復処理できるオブジェクトを提供します。

ただし、このオブジェクトを反復処理することで、**Typer**(実際にはClick)はプログレスバーを更新する必要があることを認識します。

users = ["Camila", "Rick", "Morty"]

with typer.progressbar(users) as progress:
    for user in progress:
        typer.echo(user)

ヒント

コードブロックが2レベルあることに注意してください。1つは`with`ステートメント用、もう1つは`for`ステートメント用です。

情報

これは、時間がかかる操作に特に役立ちます。

上記の例では、`time.sleep()`を使用して偽装しています。

確認してください

$ python main.py

---> 100%

Processed 100 things.

プログレスバーの`length`を設定する

ヒント

このためには、Richを使用する方がはるかに優れていることを忘れないでください。😎

プログレスバーは、反復可能オブジェクトの長さ(例:ユーザーのリスト)から生成されます。

ただし、長さがわからない場合(たとえば、毎回Web APIから新しいユーザーを取得するもの)、`typer.progressbar()`に明示的な`length`を渡すことができます。

import time

import typer


def iterate_user_ids():
    # Let's imagine this is a web API, not a range()
    for i in range(100):
        yield i


def main():
    total = 0
    with typer.progressbar(iterate_user_ids(), length=100) as progress:
        for value in progress:
            # Fake processing time
            time.sleep(0.01)
            total += 1
    print(f"Processed {total} user IDs.")


if __name__ == "__main__":
    typer.run(main)

確認してください

$ python main.py

---> 100%

Processed 100 user IDs.

`yield`を持つ関数について

上記の`yield`のようなものを見たことがない場合、それは「ジェネレーター」です。

`for`でその関数を反復処理でき、各反復で`yield`の値が提供されます。

`yield`は、複数回値を返し、`for`ループで関数を使用できるようにする`return`のようなものです。

例えば

def iterate_user_ids():
    # Let's imagine this is a web API, not a range()
    for i in range(100):
        yield i

for i in iterate_user_ids():
    print(i)

は、各「ユーザーID」(ここでは`0`から`99`までの数値)を出力します。

`label`を追加する

ヒント

このためには、Richを使用する方がはるかに優れていることを忘れないでください。😎

`label`も設定できます

import time

import typer


def main():
    total = 0
    with typer.progressbar(range(100), label="Processing") as progress:
        for value in progress:
            # Fake processing time
            time.sleep(0.01)
            total += 1
    print(f"Processed {total} things.")


if __name__ == "__main__":
    typer.run(main)

確認してください

python main.py 100個の処理が完了しました。

手動で反復処理する

何かを手動で反復処理し、プログレスバーを不規則に更新する必要がある場合は、反復可能オブジェクトを渡すのではなく、`typer.progressbar()`に`length`だけを渡すことによって行うことができます。

そして、`with`ステートメントからのオブジェクトで`.update()`メソッドを呼び出します

import time

import typer


def main():
    total = 1000
    with typer.progressbar(length=total) as progress:
        for batch in range(4):
            # Fake processing time
            time.sleep(1)
            progress.update(250)
    print(f"Processed {total} things in batches.")


if __name__ == "__main__":
    typer.run(main)

確認してください

python main.py 100個のバッチ処理が完了しました。