はじめに
(仕様の)迷宮へ誘いましょう... nikkie (UUID 28fb3f96-a221-462c-93bd-567b431715b9) です。
Python ライブラリ tqdm のtqdm.tqdm(iterable)についての気づきをしたためます。
これはすごいですよ...
目次
- はじめに
- 目次
- Python でプログレスバーを表示したい
- tqdm が返すのは、イテラブル!
- うまいと思った点:データ側でプログレスバーを出力する
- プログレスバーを出力するイテラブルの実装
- 終わりに
Python でプログレスバーを表示したい
tqdm というライブラリがあります。
大量のデータを処理する際に、進捗状況を表示してくれて大変便利です。
過去に HTTP クライアントでダウンロードする際のプログレスバーを記事にしました。
余談ですが、tqdm は標準出力・標準エラー出力に限らず、多様な出力先をサポートしています1
tqdmをdiscordで表示するのできた! pic.twitter.com/uZDSDXLYlm
— カレーちゃん (@currypurin) 2025年3月8日
tqdm が返すのは、イテラブル!
今回気づいた点です2
% uv run --with tqdm python Python 3.14.3 free-threading build >>> import tqdm >>> tqdm.__version__ '4.68.1' >>> tqdm_obj = tqdm.tqdm("abcde") 0%| | 0/5 [00:21<?, ?it/s] >>> type(tqdm_obj) <class 'tqdm.std.tqdm'> >>> from collections.abc import Iterable, Iterator >>> isinstance(tqdm_obj, Iterable) True >>> isinstance(tqdm_obj, Iterator) False
(マシンに設定している cooldown の関係で 4.68.1 と思われます)
イテラブルなので、tqdm.tqdm(iterable)はiterable同様、for文に書けます
(__next__()が実装されていないので、イテレータではないという判定です)
うまいと思った点:データ側でプログレスバーを出力する
きっかけは不慣れな clojure で数十件のデータを処理していて、進捗を知りたくなったこと。
データを処理する関数側に進捗状況を出力するコードを入れようと最初は思ったのですが、それだと色々な関数に似たような進捗出力コードが入っていきますよね。
であればデータ側に、次のデータを返すイテレータ + どこまで返したか進捗を出力するように実装したいなと思いました。
関数は変えずに、どのような関数にもプログレスバーが導入できます!
ここまで考えて、「そうか、Python の tqdm がやっているのは、イテラブルを包んで、プログレスバー出力だけ追加することなんだな」と気づきました。
プログレスバーを出力するイテラブルの実装
https://github.com/tqdm/tqdm/blob/v4.68.2/tqdm/std.py#L242
Decorate an iterable object, returning an iterator which acts exactly like the original iterable, but prints a dynamically updating progress bar every time a value is requested.
まさしく元のイテラブルのように振る舞いつつ、プログレスバーも出力します。
(tqdm 作者はイテレータと言っていますが、先のisinstanceの結果から私はイテラブルだと思っています)
__init__()の実装(抜粋)
https://github.com/tqdm/tqdm/blob/v4.68.2/tqdm/std.py#L1048
class tqdm(Comparable): def __init__(self, iterable=None, file=None, ...): self.iterable = iterable self.fp = file if not gui: # Initialize the screen printer self.sp = self.status_printer(self.fp)
(screen printer とstatus_printer、微妙に一致しないですね)
__iter__()を持つのでイテラブル!(コードは抜粋です)
https://github.com/tqdm/tqdm/blob/v4.68.2/tqdm/std.py#L1160
class tqdm(Comparable): def __iter__(self): iterable = self.iterable for obj in iterable: yield obj # Update and possibly print the progress bar. self.update(n - last_print_n)
update()を辿っていくと、sp(screen printer)を呼び出しています。
https://github.com/tqdm/tqdm/blob/v4.68.2/tqdm/std.py#L1495
self.sp(self.__str__() if msg is None else msg)
static method status_printer()でステータスを出力する関数を返しています。
https://github.com/tqdm/tqdm/blob/v4.68.2/tqdm/std.py#L437
終わりに
Python の tqdm、任意のイテラブルに、プログレスバー出力ロジックだけを追加したイテラブルとするアイデアが秀逸だと思いました。
デザインパターンの Decorator ですね3
- https://tqdm.github.io/docs/contrib.discord/ などドキュメントにもあります↩
- イテラブル(やイテレータ)については過去記事もどうぞ ↩
- マンガでわかる Decorator #デザインパターン - Qiita↩