はじめに
Pythonでiter(obj)
を呼ぶと内部的にobj.__iter__()
が実行されます。
イテレータを学ぶ中で「なぜ引数に渡したのに中の似た名前のメソッドを呼ぶのか?」と疑問に思い、その整理を記事にまとめました。
特殊メソッドと組み込み関数の対応
Pythonには「特殊メソッド」と呼ばれる仕組みがあります。
組み込み関数は、内部でこれらのメソッドを呼ぶように設計されています。
len(obj)
→obj.__len__()
str(obj)
→ obj.__str__()
iter(obj)
→ obj.__iter__()
つまりiter(obj)
は「objが持つ__iter__()
を探して呼ぶ」関数なのです。
実際に確認してみる
class MyNumbers:
def __iter__(self):
print("__iter__ が呼ばれた")
return iter([1, 2, 3])
obj = MyNumbers()
it = iter(obj) # ここで __iter__ が実行される
print(next(it)) # 1
__iter__ が呼ばれた
1
iter(obj)
と書いた瞬間に、内部でobj.__iter__()
が呼ばれているのが分かります。
for文の裏側
for文も同じ仕組みです。
for x in range(3):
print(x)
実際には以下とほぼ同じ処理が行われています。
r = range(3)
it = iter(r) # r.__iter__() が呼ばれる
while True:
try:
x = next(it)
except StopIteration:
break
print(x)
割り切ることが理解の近道
「なんで引数に渡したオブジェクト自身の__iter__
を呼ぶのか?」と考えると不思議ですが、これはPythonの仕様です。
iter(obj)
は「obj.__iter__()
を呼ぶための関数」。
さらに突き詰めると、CPythonのC言語実装の中で
iter()
が __iter__
を探して呼ぶように定義されているためこう動きます。
ユーザー視点では、普段使う上で「仕様としてそう決まっている」と理解しておけば十分です。
自作クラスをfor文で回してみる
この仕組みを利用すれば自作クラスもfor文で扱えるようになります。
class MyNumbers:
def __iter__(self):
return iter([10, 20, 30])
for n in MyNumbers():
print(n)
10
20
30
このコードの裏側で起きていること
-
MyNumbers()
でインスタンスを作る(例:obj = MyNumbers()
) -
for
文が最初にiter(obj)
を呼ぶ - そこで
obj.__iter__()
が実行される -
iter([10, 20, 30])
が返ってくる(リストのイテレータ) - そのイテレータから
next()
が繰り返し呼ばれる - その後
StopIteration
が発生してループ終了 - その結果、
print(n)
で10, 20, 30
が順番に出力される
まとめ
-
iter(obj)
を呼ぶとobj.__iter__()
が実行される -
これはPythonの特殊メソッドの仕組みによるもの
-
for文や
in
もこの仕様に基づいて動いている -
理解のポイントは「不思議がらず仕様として割り切ること」