【Python】長時間処理の「いつ終わるの？」問題を解決！プログレスバー実装を自作からライブラリまで3段階の備忘録

## はじめに
長時間かかる処理を実行するとき、「あとどのくらいで終わるの？」という不安を感じたことはありませんか？プログレスバーがあれば、進捗が一目でわかり、ユーザー体験が格段に向上します。

この記事では、プログレスバーを3つのレベルで実装し、それぞれの使い分けを備忘録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/d53f286c-b006-45ef-b0a4-5983d3b252b7.png)

---

## 1. 10行で作るシンプルなプログレスバー

まずは仕組みを理解するため、最小限のコードでプログレスバーを作ってみましょう。

```python
import time, sys
def progress_bar(total, current):
    progress = current / total
    filled = int(50 * progress)
    bar = '█' * filled + '-' * (50 - filled)
    sys.stdout.write(f'\r[{bar}] {progress*100:.1f}%')
    sys.stdout.flush()

# デモ実行
print("シンプルなプログレスバー:")
for i in range(101):
    progress_bar(100, i)
    time.sleep(0.02)
print("\n✅ 完了！")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/b9c1676d-56d3-40bf-9d89-f953a195aa78.png)

このコードは進捗率を視覚的に表示する基本的な仕組みを示しています。`\r`で行の先頭に戻り、`sys.stdout.flush()`で即座に表示を更新することで、動的な表示を実現しています。

---

## 2. 実用的なProgressBarクラス

次に、残り時間の予測機能を追加したクラスベースの実装を見てみましょう。

```python
import time, sys
from datetime import timedelta

class ProgressBar:
    def __init__(self, total, desc="進捗"):
        self.total = total
        self.desc = desc
        self.current = 0
        self.start_time = time.time()
        
    def update(self, current):
        self.current = current
        progress = current / self.total
        filled = int(40 * progress)
        bar = '█' * filled + '░' * (40 - filled)
        
        elapsed = time.time() - self.start_time
        if current > 0:
            eta = int((elapsed / progress) - elapsed)
            eta_str = str(timedelta(seconds=eta))
        else:
            eta_str = "計算中"
            
        sys.stdout.write(f'\r{self.desc}: [{bar}] {progress*100:5.1f}% | '
                        f'残り: {eta_str}')
        sys.stdout.flush()
    
    def finish(self):
        self.update(self.total)
        print()

# デモ実行
print("時間予測付きプログレスバー:")
pbar = ProgressBar(80, "データ処理")
for i in range(81):
    pbar.update(i)
    time.sleep(0.05)
pbar.finish()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/d9e31d85-4d05-4829-9255-ff23f6e856d5.png)

このクラスでは、経過時間と現在の進捗率から残り時間を計算し、ユーザーにより詳しい情報を提供します。中規模な処理で威力を発揮します。

---

## 3. tqdmでプロフェッショナルなプログレスバー

本格的な開発では、豊富な機能を持つ`tqdm`ライブラリを使用しましょう。

```python
try:
    from tqdm import tqdm, trange
except ImportError:
    import subprocess, sys
    subprocess.check_call([sys.executable, "-m", "pip", "install", "tqdm"])
    from tqdm import tqdm, trange
import time

# 基本的な使い方
print("tqdm基本:")
for i in tqdm(range(60), desc="処理中"):
    time.sleep(0.03)

# ネストしたプログレスバー（機械学習風）
print("\n機械学習風ネストバー:")
for epoch in trange(3, desc="エポック"):
    for batch in trange(20, desc=f"エポック{epoch+1}", leave=False):
        time.sleep(0.03)

# リアルタイム情報表示
print("\nリアルタイム情報:")
with tqdm(range(50), desc="処理", unit="item") as pbar:
    for item in pbar:
        time.sleep(0.04)
        rate = pbar.format_dict.get('rate') or 0
        pbar.set_postfix({"値": item, "速度": f"{rate:.1f}it/s"})
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/36a7f6d1-e248-4bb8-9706-67e318db2f3a.png)

`tqdm`は機械学習のトレーニングループやデータ処理パイプラインなど、複雑な処理に最適です。ネストしたプログレスバーやリアルタイム情報表示など、高度な機能が簡単に利用できます。

---

## まとめ

### 使い分け指針

| 用途 | 手法 | 特徴 |
|------|------|------|
| 学習用 | 自作関数 | 仕組み理解に最適 |
| 中規模処理 | ProgressBarクラス | 時間予測機能付き |
| 本格開発 | tqdm | 高機能で安定した動作 |

### 実装のポイント

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/5f033f6e-4cbb-4a63-80a3-e2464345e0c5.png)

プログレスバーを効果的に活用するには、いくつかの重要なポイントがあります。まず更新頻度に注意が必要で、あまり頻繁に更新するとパフォーマンスが低下するため、適切な間隔での更新を心がけましょう。また、単純な進捗率だけでなく、残り時間や処理速度といった有用な情報も併せて表示することで、ユーザーの体験が大幅に向上します。さらに、例外が発生した場合でもプログレスバーを適切に終了させるエラー対応も忘れずに実装しておくことが大切です。
これらのポイントを押さえてプログレスバーを活用すれば、ユーザーにとって親しみやすく、使いやすいPythonアプリケーションを作ることができます！