Python 速習チュートリアル

Python のイテレータ (Iterators)

1. Python イテレータ

イテレータ(Iterator)は、数えられる数の値を含むオブジェクトです。
イテレータは、すべての値を反復(イテレート)できるオブジェクト、つまり、その上を走査できるオブジェクトを指します。

技術的な定義では、Python におけるイテレータとは、イテレータプロトコル(Iterator Protocol)を実装したオブジェクトであり、__iter__()__next__() というメソッドで構成されています。

2. イテレータ vs イテラブル

リスト(List)、タプル(Tuple)、辞書(Dictionary)、セット(Set)はすべてイテラブル(Iterable)なオブジェクトです。これらは、そこからイテレータを取得できる「イテラブルな容器」のような存在です。

これらのオブジェクトはすべて、イテレータを取得するために使用される iter() メソッドを持っています。

2.1 タプルからのイテレータ取得

タプルからイテレータを取得し、各値を順次出力する例です。

mytuple = ("リンゴ", "バナナ", "チェリー")
myit = iter(mytuple)

print(next(myit)) # リンゴ
print(next(myit)) # バナナ
print(next(myit)) # チェリー

2.2 文字列のイテレーション

文字列(String)もイテラブルなオブジェクトであり、一連の文字で構成されています。そのため、文字列からもイテレータを取得することが可能です。

mystr = "バナナ"
myit = iter(mystr)

print(next(myit))
print(next(myit))
print(next(myit))

3. イテレータのループ処理

for ループを使用することで、イテラブルなオブジェクトを効率的に走査できます。

3.1 リストの走査

mylist = ["リンゴ", "バナナ", "チェリー"]

for x in mylist:
  print(x)

3.2 タプルの走査

mytuple = ("リンゴ", "バナナ", "チェリー")

for x in mytuple:
  print(x)

実際には、for ループは背後でイテレータオブジェクトを作成し、各ループごとに next() メソッドを実行しています。

4. カスタムイテレータの作成

独自のクラスをイテレータとして定義するには、__iter__()__next__() メソッドをそのクラスに実装する必要があります。

Python のクラス設計において、すべてのクラスには __init__() という初期化メソッドがありますが、イテレータの実装には以下のメソッドが重要になります。

  • __iter__(): __init__() と同様に初期化処理を行えますが、最終的にイテレータオブジェクト自体(通常は self)を返す必要があります。
  • __next__(): 次のアイテムを返すための操作を記述します。

4.1 数値を生成するイテレータの実装

1 から始まり、呼び出されるたびに数値を 1 ずつ増加させるイテレータの例です。

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self

  def __next__(self):
    x = self.a
    self.a += 1
    return x

myclass = MyNumbers()
myiter = iter(myclass)

print(next(myiter)) # 1
print(next(myiter)) # 2
print(next(myiter)) # 3
print(next(myiter)) # 4
print(next(myiter)) # 5

5. StopIteration による終了制御

上記の例では、next() を呼び出し続ければ数値は無限に増え続けます。
特に for ループで利用する場合、適切なタイミングで反復を終了させるために StopIteration ステートメントを使用します。

__next__() メソッド内で条件判定を行い、指定した回数に達した際にエラーを発生させることで、ループを安全に停止させることができます。

5.1 20回で停止するイテレータの実装

20回繰り返した後に自動で停止する設定を加えた例です。

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self

  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      # 反復を終了させる例外を発生させる
      raise StopIteration

myclass = MyNumbers()
myiter = iter(myclass)

for x in myiter:
  print(x)