1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonAdvent Calendar 2024

Day 20

Python初心者がイテレータについて理解する

Posted at

最近、イテレータについて勉強したので書いてみようと思います。

イテレータとは、要素を反復して取り出すことの配列みたいな型です。
は?ですね、はい。

反復処理

公式ドキュメントによると、反復処理メソッドをサポートするための型と書いてあります。1
要するに、動的にオブジェクトを生成する変数(配列)みたいなものですかね。

ジェネレーター

このドキュメントにはジェネレーターなるものも書かれています。
この解説を見るに、ジェネレーターはイテレータの一種で、呼び出すごとに何か処理をするイテレータなのだとわかります。2

これに関しては後述します。

配列をイテレーターにしてみる

とりあえず、手を動かしてみました。

season = ['Spring', 'Summer', 'Fall', 'Winter']
iter_season = iter(season) # イテレーターに変換
print(type(iter_season)) # ここでiterと出る。イテレーターになっている。

print(next(iter_season)) # 1番目のイテレータを表示後、次のイテレータに進む
next(iter_season) # 次のイテレータに進む

# イテレータを1つずつ取り出して表示
for i in iter_season:
    print(i)

# イテレータを1つずつ取り出して表示
for i in iter_season:
    print(i)

これをすると、

<class 'list_iterator'>
Spring
Fall
Winter

となります。

なんでしょうか...感覚としては一度きりしか呼び出せない配列のような気分です。

reversed関数

iter()ではただイテレーターにするだけでしたが、reversed()ではどうでしょう。

season = ['Spring', 'Summer', 'Fall', 'Winter']
iter_season = reversed(season)
print(type(rev_season)) # 型を表示して確認

print(next(iter_season)) # 1番目のイテレータを表示後、次のイテレータに進む
next(iter_season) # 次のイテレータに進む

# イテレータを1つずつ取り出して表示
for i in iter_season:
    print(i)

# イテレータを1つずつ取り出して表示
for i in iter_season:
    print(i)
<class 'list_reverseiterator'>
Winter
Summer
Spring

逆になっているのがわかりますね。
reversedを使うと逆向きのイテレータが作ることができるようです。

注意点

先ほども書いたように、イテレータオブジェクトは使い捨てなので、一度呼び出し切った場合はもう一度作成し直さなければならないようです。

自作のイテレータクラス

ここまでは標準で用意されている型をイテレータに変換してきましたが、今度は自分で作ったクラスをイテレータとして実装してみましょう。

class MyIter:
    def __init__(self, objs):
        self.objs = objs # コンストラクタの引数をオブジェクトとして持つ
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.objs):
            raise StopIteration # 配列の中身がなくなったらStopIteration例外を投げる
        value = self.objs[self.index] # index番めの要素をセット
        index = self.index # indexをセット
        self.index += 2 # indexを増やす。次に next()されたときは増えた状態になっている。
        return index, value

my_iter = MyIter(['Apple','Orange','Strawberry','Peach','Banana'])
for i in my_iter:
    print(i)

こうすると、結果は、

(0,'Apple')
(2,'Strawberry')
(3,'Banana')

となります。

ジェネレーターとはなんぞや

ジェネレーターとは、さっきのやつの関数バージョンのようなことらしいです。

def MyGen(obj):
    index = 0
    while True:
        if self.index >= len(self.obj):
            raise StopIteration
        value = self.obj[self.index]
        index = self.index
        yield index, value

my_gen = MyGen(['Apple','Orange','Strawberry','Peach','Banana'])
print(my_gen[0])
print(my_gen[1])

とすると、結果は、

(0,'Apple')
(2,'Strawberry')

と同じように返ってきます。

先のものとの違いは、yieldで値を返すことです。

これの何が嬉しいかというと、今回であればいつ呼び出されても同じ処理をしているが、1回目の処理と違う処理をさせることだってできることですね。

ジェネレータやイテレータの便利なところ

イテレータの便利さは一度読み取ったら、前のものはもう読み出せないことで、二重処理を防ぐことができるのではないかと思いました。
ジェネレーターに関しては、メモリを取らないことのようです。処理をして値を生成するわけだが、呼び出されない限り処理はされないので、メモリに優しいことのようですね。

自分はまだ完璧に使いこなすことはできませんが、これからも勉強していこうと思います。

  1. https://docs.python.org/2/library/stdtypes.html#iterator-types

  2. https://docs.python.org/2/reference/expressions.html#yieldexpr

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?