Pythonでiter関数を使ったイテレーターの作り方や使い方を解説する

スポンサーリンク

今回は、Pythonのiter()関数の使い方を紹介します。

イテレータとは、それ自体がPythonのオブジェクトをロードするオブジェクトのことで、しばしば使いたいと思うことがあります。

しかし、配列やリストとは対照的に、イテレータは必要なときに必要な分だけオブジェクトをロードするだけです。

これはレイジーローディング、またはストリームベースローディングと呼ばれます。

これは、メモリを節約したい場合や、オブジェクトが非常に大きい場合にオブジェクト全体を一度にロードしない場合に、非常に便利です。


スポンサーリンク

Pythonの基本的な構文 iter()

iter()` 関数を使うと、辞書やリスト、セットなどの反復可能なオブジェクトへのイテレータを生成することができます

Pythonの iter() 関数の基本的な使い方は以下の通りです。

iterator = iter(iterable)

これは iterable オブジェクトから iterator を生成します。

そして、 next(iterator) を使って、 StopIteration 例外を発生させるまでオブジェクトを一つずつロードしていきます。

また、同じイテレータを使用して、再びイテレートすることはできないことに注意してください。

イテレートする前に、Pythonの iter() を使って別のイテレータを生成しなければなりません。


Python の iter() を使う – 簡単な例

ここでは、iter() を使った簡単な例を紹介します。

10個の要素からなるリストを受け取り、1つずつロードしていきます。

a = [i for i in range(10)]
 
iterator = iter(a)
 
while True:
    try:
        out = next(iterator) # Load the next element
        print(f"Iterator loaded {out}")
    except StopIteration:
        # End of loading. Break out of the while loop
        print("End of iterator loading!")
        break

結果は以下の通りです。

Iterator loaded 0
Iterator loaded 1
Iterator loaded 2
Iterator loaded 3
Iterator loaded 4
Iterator loaded 5
Iterator loaded 6
Iterator loaded 7
Iterator loaded 8
Iterator loaded 9
End of iterator loading!

見てわかるように、確かに、StopIteration 例外をキャッチするまで、リストから要素を 1 つずつロードしています!


Python の iter() をカスタムオブジェクトに使用する

先ほど述べたように、Pythonのiter()は反復可能なオブジェクトであれば、どのようなオブジェクトでも使うことができます。

これは、いくつかの条件を満たせば、カスタムオブジェクトにも適用されます。

しかし、Pythonで任意のオブジェクトが反復可能であるための条件とは何でしょうか?

  • そのオブジェクトのクラスは __iter__() メソッドを持たなければなりません。
  • そのオブジェクトのクラスが __next__() メソッドを持っていること。また、終了条件に達した場合は StopIteration Exception も発生させることが推奨されます。

さて、Python の iter() メソッドはイテレータを構築して、 __iter__() メソッドを呼び出します。

同様に、 next(iterator) はフードの裏で __next__() メソッドを呼び出します。

注意: これらのメソッドを持たないクラスは、少なくとも __getitem()__ メソッド (引数は 0 から整数) を持つ必要があります。

それでは、ある制限値まで整数を生成するカスタムオブジェクトのクラスを書いてみましょう。

class MyClass():
    def __init__(self, max_val):
        # Set the maximum limit (condition)
        # max_val must be a natural number
        assert isinstance(max_val, int) and max_val >= 0
        self.max_val = max_val
    def __iter__(self):
        # Called when the iterator is generated
        # Initialise the value to 0
        self.value = 0
        return self
    def __next__(self):
        # Called when we do next(iterator)
        if self.value >= self.max_val:
            # Terminating condition
            raise StopIteration
        self.value += 1
        # Return the previously current value
        return self.value - 1
 
# Set the limit to 10
my_obj = MyClass(10)
 
# An iterator to the object
my_iterator = iter(my_obj)
 
while True:
    try:
        val = next(my_obj)
        print(f"Iterator Loaded {val}")
    except StopIteration:
        print("Iterator loading ended!")
        break

結果は以下の通りです。

Iterator Loaded 0
Iterator Loaded 1
Iterator Loaded 2
Iterator Loaded 3
Iterator Loaded 4
Iterator Loaded 5
Iterator Loaded 6
Iterator Loaded 7
Iterator Loaded 8
Iterator Loaded 9
Iterator loading ended!

見ての通り、カスタムオブジェクトで iter() 関数を使用することができます

iter__()メソッドはイテレータオブジェクトを生成し、next()` を使ってそれを更新します。

終了条件は、現在の値が最大値よりも大きいときで、このときに StopIteration 例外を発生させます。


iter()でセンチネル値まで値を生成する

Python の iter() には、もう一つ引数を渡すことができます。

この2つ目の引数は sentinel 要素と呼ばれます。

このセンチネル要素を渡すと、生成された値がこのセンチネル値と等しくなるまでイテレータは値を生成し続け、それ以降は StopIteration が発生します。

この後、イテレータの生成は自動的に停止する!

これは、関数から連続したデータが送られてくる場合に非常に便利です。

また、センチネル引数を使用する場合、最初の引数は呼び出し可能でなければならないので、関数も必要です。

iterator = iter(callable_object, sentinel)

ここで、 iterator はイテレータであり、戻り値が sentinel と等しくなるまで callable_object を呼び続けることができる。

ここで、 callable_object は関数やメソッド、あるいはラムダであってもよい。

Lambda を callable として使用する簡単な例を見てみましょう。

文字列を入力として、ラムダ関数に渡し、改行のセンチネル要素(‘˶’ᵕᴗᵕ’)になるまで値を生成し続けるというものです。

a = "This is a long string consisting of two lines.
This is the second line.
This is the third line."
 
start = 0
size = 1
 
def func(a):
    return a[start: start+size]
 
iterator = iter(lambda: func(a), '
'
)
 
# Will generate values until '
'
for out in iterator:
    print(f"Iterator loaded {out}")
    start += size
 
print("Encountered Newline!")

結果は以下の通りです。

Iterator loaded T
Iterator loaded h
Iterator loaded i
Iterator loaded s
Iterator loaded
Iterator loaded i
Iterator loaded s
Iterator loaded
Iterator loaded a
Iterator loaded
Iterator loaded l
Iterator loaded o
Iterator loaded n
Iterator loaded g
Iterator loaded
Iterator loaded s
Iterator loaded t
Iterator loaded r
Iterator loaded i
Iterator loaded n
Iterator loaded g
Iterator loaded
Iterator loaded c
Iterator loaded o
Iterator loaded n
Iterator loaded s
Iterator loaded i
Iterator loaded s
Iterator loaded t
Iterator loaded i
Iterator loaded n
Iterator loaded g
Iterator loaded
Iterator loaded o
Iterator loaded f
Iterator loaded
Iterator loaded t
Iterator loaded w
Iterator loaded o
Iterator loaded
Iterator loaded l
Iterator loaded i
Iterator loaded n
Iterator loaded e
Iterator loaded s
Iterator loaded .
Encountered Newline!

このように、イテレータは改行があるまで値を生成しています。

同じプログラムを while ループを使って行い、 StopIteration 例外をキャッチすることもできました。

これは、関数から返される出力のブロックを処理したい場合に非常に便利です。


まとめ

この記事では、Pythonの iter() 関数を使用して、さまざまなオブジェクトの反復表を生成する方法について見てきました。

タイトルとURLをコピーしました