單一個下載工作,print 一下進度確實夠用。但當有十個片段同時並行下載,每個片段各自 print 自己的進度,終端機畫面會變成一團交錯覆蓋、無法閱讀的文字流,反而看不出整體進度到哪。
❌ 反例:每個工作各自直接印
async def save_segment(index, total):
print(f"downloading {index}/{total}") # 十個並行工作各自印,互相干擾
✅ 正例:統一的進度條輸出函式,格式一致、可控制輸出方式
def progressbar(size: int, total: int, title: str = "Progress"):
filled = int(size * 20 / total)
bar = '█' * filled + ' ' * (20 - filled)
print(f'\r[{title}]:[{bar}]{size / total * 100:.2f}%', end='')
async def save_segment(index, total, directory):
progressbar(index, total, f'download: {directory}')
把輸出邏輯收斂成一個共用函式,好處不只是格式一致——它讓「進度怎麼呈現」變成一個可以獨立替換的實作:現在是印到終端機,之後如果要改成寫進日誌檔、丟到某個監控系統,只需要換掉 progressbar 這個函式的實作,呼叫端完全不用改。
同樣的道理也適用在錯誤與警告訊息上。與其在程式碼各處直接 print,統一透過一個 Logger 類別(區分 info/warning/error/success 等等級,各自對應不同顏色或格式)來輸出,好處是呼叫端只需要表達「這是一則什麼等級的訊息」,不需要關心這則訊息最後會被印成什麼樣子。這也讓測試變得容易——測試時可以換一個假的 Logger 實作,只驗證「有沒有記錄到某則訊息」,不用真的去解析終端機輸出的文字。
你的專案裡的進度/日誌輸出,是每個地方各自直接 print,還是有一層統一的抽象?如果現在要把輸出方式從終端機改成寫進檔案,你要改幾個地方?
明天回到重試策略:失敗要退避重試,但退避的時間該怎麼選。