Pythonでprintの上書きをする方法

Pythonでprint出力を上書きする方法を解説。キャリッジリターンによる一行上書きと、ANSIエスケープシーケンスを使った複数行の上書き方法を紹介します。

Pythonでprintの上書きをする方法

一行上書きする場合

一行を上書きする場合、\r(キャリッジリターン)を使用します。これはカーソルを行の先頭に戻す制御文字です。

print("\r"+"一行を上書きします!",end="")

end=""を使用して改行を行わないようにします。

カウントダウンタイマーの例

\rの実践的な使い方として、カウントダウンタイマーを実装してみます。

import time
import sys

def countdown(seconds):
    for i in range(seconds, 0, -1):
        # \rで行頭に戻り、上書き表示
        sys.stdout.write(f"\r残り {i:3d} 秒")
        sys.stdout.flush()
        time.sleep(1)
    sys.stdout.write("\r完了!          \n")

countdown(10)

ここでsys.stdout.writesys.stdout.flush()を使っています。printは内部的にsys.stdout.writeを呼び出しますが、sys.stdoutを直接使うことでバッファリングの制御がより明確になります。flush()を呼ぶことで、出力が即座に画面に反映されます。

注意点: 前回の出力より短い文字列を上書きすると、前の文字が残ってしまいます。上の例では"完了!"の後にスペースを入れて、前の表示を確実に消しています。

スピニングカーソルの実装

処理中であることを視覚的に示すスピニングカーソル(ローディングアニメーション)も\rで実装できます。

import sys
import time
import itertools

def spinning_cursor(duration=5):
    spinner = itertools.cycle(['|', '/', '-', '\\'])
    end_time = time.time() + duration
    while time.time() < end_time:
        sys.stdout.write(f"\r処理中... {next(spinner)}")
        sys.stdout.flush()
        time.sleep(0.1)
    sys.stdout.write("\r処理完了!     \n")

spinning_cursor(3)

itertools.cycleを使うことで、スピナー文字を無限に繰り返すことができます。

複数行を上書きする場合

複数行を上書きするような表示を行う場合、特殊なエスケープシーケンス\033[nA(カーソルをn行上に移動)などを利用します。

print("最初の行")
print("二番目の行")
# カーソルを1行上に移動し、上書き
print("\033[1A新しい二番目の行")

ANSIエスケープシーケンス一覧

ターミナル上のカーソル制御や装飾に使えるANSIエスケープシーケンスの主要なものを表にまとめます。

カーソル操作

シーケンス説明使用例
\033[nAカーソルをn行上に移動print("\033[2A")
\033[nBカーソルをn行下に移動print("\033[1B")
\033[nCカーソルをn列右に移動print("\033[5C")
\033[nDカーソルをn列左に移動print("\033[3D")
\033[2K現在の行をクリアprint("\033[2K", end="")
\033[Jカーソル以降をクリアprint("\033[J", end="")
\033[Hカーソルを画面左上に移動print("\033[H")
\033[{r};{c}Hカーソルを行r, 列cに移動print("\033[5;10H")

テキスト装飾(色・スタイル)

シーケンス説明
\033[0mリセット(装飾解除)
\033[1m太字
\033[4m下線
\033[31m赤色テキスト
\033[32m緑色テキスト
\033[33m黄色テキスト
\033[34m青色テキスト
\033[41m赤色背景
\033[42m緑色背景

例えば、色付きのステータス表示は以下のように書けます。

# 成功時は緑、失敗時は赤で表示
def print_status(message, success=True):
    color = "\033[32m" if success else "\033[31m"
    reset = "\033[0m"
    print(f"{color}{message}{reset}")

print_status("テスト通過", success=True)
print_status("テスト失敗", success=False)

ダウンロード進捗表示の実装例

\rとANSIエスケープシーケンスを組み合わせて、実用的なダウンロード進捗表示を実装してみます。

import sys
import time

def download_progress(total_size, chunk_size=1024):
    """ダウンロード進捗をプログレスバーで表示する"""
    downloaded = 0
    bar_length = 40

    while downloaded < total_size:
        downloaded += chunk_size
        if downloaded > total_size:
            downloaded = total_size

        # 進捗率の計算
        progress = downloaded / total_size
        filled = int(bar_length * progress)
        bar = "█" * filled + "░" * (bar_length - filled)

        # MB単位で表示
        dl_mb = downloaded / (1024 * 1024)
        total_mb = total_size / (1024 * 1024)

        # 色付きで進捗表示
        if progress < 0.5:
            color = "\033[33m"  # 黄色
        else:
            color = "\033[32m"  # 緑色
        reset = "\033[0m"

        sys.stdout.write(
            f"\r{color}[{bar}]{reset} "
            f"{progress:6.1%} "
            f"({dl_mb:.1f}/{total_mb:.1f} MB)"
        )
        sys.stdout.flush()
        time.sleep(0.01)  # ダウンロードのシミュレーション

    sys.stdout.write("\n")
    print("ダウンロード完了!")

# 10MBのファイルをシミュレーション
download_progress(10 * 1024 * 1024, chunk_size=100 * 1024)

複数行リアルタイム更新の例

ANSIエスケープシーケンスを組み合わせると、複数のタスクの進捗を同時にリアルタイム更新できます。

import sys
import time
import random

def multi_task_progress():
    """複数タスクの進捗を同時更新する"""
    tasks = ["データ取得", "前処理  ", "学習    ", "評価    "]
    progress = [0] * len(tasks)

    # 初期表示
    for task in tasks:
        print(f"  {task}: [{'░' * 30}]   0%")

    while not all(p >= 100 for p in progress):
        # ランダムに進捗を更新
        for i in range(len(tasks)):
            if progress[i] < 100:
                progress[i] = min(100, progress[i] + random.randint(0, 5))

        # カーソルをタスク数分上に移動
        sys.stdout.write(f"\033[{len(tasks)}A")

        for i, task in enumerate(tasks):
            filled = int(30 * progress[i] / 100)
            bar = "█" * filled + "░" * (30 - filled)
            if progress[i] >= 100:
                color = "\033[32m"  # 完了: 緑
            else:
                color = "\033[33m"  # 進行中: 黄
            reset = "\033[0m"
            print(f"  {task}: {color}[{bar}]{reset} {progress[i]:3d}%")

        time.sleep(0.1)

multi_task_progress()

クロスプラットフォーム対応

ANSIエスケープシーケンスはLinuxやmacOSのターミナルではそのまま動作しますが、Windowsの従来のコマンドプロンプト(cmd.exe)では対応していません

Windowsでの対応方法

Windows 10以降のWindows Terminalや PowerShellではANSIエスケープシーケンスがサポートされていますが、古い環境との互換性を保つにはcoloramaライブラリを使います。

# pip install colorama
from colorama import init, Fore, Style

# WindowsでANSIエスケープを有効化
init()

# coloramaを使った色付き出力
print(Fore.GREEN + "成功" + Style.RESET_ALL)
print(Fore.RED + "エラー" + Style.RESET_ALL)

colorama.init()を呼ぶだけで、WindowsでもANSIエスケープシーケンスが正しく動作するようになります。Linux/macOSでは何もしない(副作用がない)ため、クロスプラットフォームなコードに適しています。

os.nameによる分岐

coloramaを使わない場合は、os.nameで分岐する方法もあります。

import os
import sys

def supports_ansi():
    """ANSIエスケープシーケンスが使えるか判定"""
    if os.name == "nt":
        # Windows 10 build 10586以降はサポート
        return os.environ.get("WT_SESSION") is not None  # Windows Terminal
    return hasattr(sys.stdout, "isatty") and sys.stdout.isatty()

if supports_ansi():
    GREEN = "\033[32m"
    RESET = "\033[0m"
else:
    GREEN = ""
    RESET = ""

print(f"{GREEN}ステータス: OK{RESET}")

ライブラリ比較:手動 vs tqdm vs rich

用途に応じて適切な方法を選びましょう。

特性手動(\r / ANSI)tqdmrich
外部依存なしpip installpip install
プログレスバー自作が必要1行で実装可能1行で実装可能
複数バー同時表示自作が必要サポートサポート
テーブル表示不可不可サポート
色付き出力ANSIコード手動限定的Rich Markup で簡単
Windows対応colorama必要自動対応自動対応
カスタマイズ性完全に自由中程度高い
学習コスト低い(原理理解向き)低い中程度
適したケース軽量な用途、学習目的ループ処理リッチなCLIアプリ

シンプルな上書き表示には本記事の方法を、ループ処理のプログレスバーにはtqdmを、リッチなCLIアプリにはrichの利用を検討してください。

モダンなPython出力ライブラリ

printの上書き以外にも、リッチな出力を実現するライブラリがあります。

richライブラリ

richはターミナルに色付きテキスト、テーブル、プログレスバーなどを表示できるライブラリです。

from rich.console import Console
from rich.table import Table

console = Console()

# プログレス表示
from rich.progress import track
import time

for i in track(range(100), description="Processing..."):
    time.sleep(0.01)

# ステータス表示(スピナー付き)
with console.status("Computing..."):
    time.sleep(2)
    console.print("[bold green]Done![/bold green]")

tqdmによるプログレスバー

tqdmは簡潔な記述でプログレスバーを表示できます。上書き表示の代替として広く使われています。

from tqdm import tqdm
import time

for i in tqdm(range(100)):
    time.sleep(0.01)

関連記事


関連ツール