5.1 Iterable
と Iterator
すでに知っていると思うけど、イテレーターというのは イテレーターのプロトコルを実装しているオブジェクトで、 コレクションから順番に要素を取得することができるんだ。 Pythonでは、リスト、タプル、文字列のようなシーケンスの要素を反復処理するために広く使われているよ。
イテレーターの仕組みと使い方を見てみよう。
イテラブルオブジェクト (Iterable)
for
ループでオブジェクトを回すためには、そのオブジェクトが
イテラブルでなければならない – Iterable
ということだね。つまり、
オブジェクトはメソッド__iter__()
を
実装している必要があるよ。そのメソッドはイテレーターオブジェクトを返すべきだね。
イテレーターオブジェクト (Iterator)
これは特殊なオブジェクトで、シーケンスの
次の要素を返すメソッド __next__()
を持っているんだ。
要素が終わると、メソッド__next__()
は
StopIteration
例外を投げて
反復の終了を示すよ。
イテレーターはまた、メソッド__iter__()
を実装して、
自身のイテレーターを返さなければならない。
Pythonの組み込み関数を使用した例
この例では、リストnumbersが
イテラブルオブジェクトであるんだ。イテレーターを
iter()
関数を使って取得し、
next()
関数を使って
要素を反復処理するんだ。StopIteration
例外が
投げられるまでね。
# イテラブルオブジェクト
numbers = [1, 2, 3, 4, 5]
# イテラブルオブジェクトからイテレーターを取得
iterator = iter(numbers)
# イテレーターを使って要素を反復処理
try:
while True:
number = next(iterator)
print(number)
except StopIteration:
pass
これはまさにこういうコードを書くときに起こることだね:
# イテラブルオブジェクト
numbers = [1, 2, 3, 4, 5]
for number in numbers:
print(number)
5.2 イテレーターの本質
イテレーターは要素のグループを順番に巡るのを助けてくれるオブジェクトだよ。実装はさまざまだけど、イテレーターに求められる すべての要件を実装する自分のクラスを書いてみよう。
ステップ 1. まずは自分のクラスを作成
start
からend
までの数字を順番に返すようにしよう
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
ステップ 2. __iter__
メソッドのサポート
ここでは__iter__
メソッドを追加して、__next()__
メソッドを呼び出すイテレーターオブジェクトを返すようにするんだ。
自分のオブジェクトの参照を返しても問題ないよ。
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
ステップ 3. __next__
メソッドのサポート
イテレーターオブジェクトに次の要素を返すメソッド__next__
を追加する必要があるね。
変数current
を使って単に次の要素を返すよ:
def __next__(self):
current = self.current
self.current += 1
return current
ステップ 4. イテレーターを停止する
イテレーターがすでにすべての予定されていた値を返したら、例外StopIteration
を投げる必要があるね。
最後の関数を少し修正しよう:
def __next__(self):
if self.current >= self.end:
raise StopIteration
current = self.current
self.current += 1
return current
いい感じ。これでイテレーターを使えるようになったね。ここに全体のコードの例があるよ:
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
current = self.current
self.current += 1
return current
# カスタムイテレーターのインスタンスを作成
my_iter = MyIterator(1, 5)
# イテレーターを使って要素を反復処理
for num in my_iter:
print(num)
5.3 正しいイテレーター
前の例のイテレーターの何が悪いのか?そうだね、動くけど少し単純すぎるんだ。同じ要素のコレクションを複数のイテレーターで同時に巡ることができない。
メソッド__iter__
で自身を返すのではなく、別のオブジェクトを返すコードを書くのが正しいだろうね。それがすべての要素を正しく返すようにするんだ。
例:
class MyIterable:
def __init__(self, data):
self.data = data
def __iter__(self):
return MyIterator(self.data)
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
item = self.data[self.index]
self.index += 1
return item
# 使用例
my_iterable = MyIterable([1, 2, 3, 4])
for item in my_iterable:
print(item)
この例では、2つのクラスがあるね — 最初のクラスにはイテレーターで進むコレクションが渡されるんだ。2番目のクラスがイテレーターで、メソッドnext()
でコレクションの要素を返すんだ。これも非常に簡単だけど、これがクラスにイテレーターを追加する方法だよ。
GO TO FULL VERSION