Pythonでfor文について調べていくと必ずでてくるのがイテレータ。
なんだかわかったようでわからない謎な解説ばかりです。
そんなわけでピストルで解説します(もっと謎)。
弾を詰めて、引き金を引くと一発ずつ発射し、すべてのたまを撃ち終わると終了という普通のピストルです。5発の弾が入るピストルなら5発打つまでは繰り返し、撃つことができます。
イテレーションとは繰り返すという意味です。辞書をひくと繰り返しとでてきます。
で、イテレーションとイテレータは微妙に違います。実はイテラブルという言葉もあって混乱しがちなのですが、まずは登場人物をあげてみます。
➀イテレーション
➁イテラブル
➂イテレータ
➀ イテレーション
一番攻略しやすいのはイテレーションです。
イテレーションとは先程述べたように「繰り返し」という意味になります。
たとえば
for i in range(5):
処理
と書けば処理を5回繰り返しますが、この繰返しのことをイテレーションといいます。
➁イテラブル
次に攻略しやすいのはイテラブルです、繰り返しの元を提供するものということで
ピストルの例えでいうと、リボルバーや弾倉とでも言ったらいいでしょうか
たとえば
a = [1, 2, 3, 4, 5]
for i in a:
処理
と書いたときの
配列a
がイテラブルです。
➂ イテレータ
最後に真打ちのイテレータですが、こいつは装填済み発射寸前の銃本体みたいなイメージです。引き金が引かれるのを待つばかりといった感じです。(この文、コメントの指摘を受けて修正しました)
Pythonではイテラブルをiter()関数に突っ込んだ結果のオブジェクトがイテレータです。イテラブルが配列aなら iter(a)がイテレータになります。
たとえば
it = iter(a)
とかくと itがイテレータになります。
引き金に相当するのがnext()関数で、
it.next()とすると弾丸が発射されるイメージです。
配列aの中の要素が1発ずつ発射されていきます。
実際にやってみましょう
Jupyter notebookで
a = [1,2,3,4,5]
と入力します。ここでクイズですが、aはなんだったでしょうか
答えはイテラブルですね。
その次のセルに
it = iter(a)
と入力します。ここでまたクイズです。itはなんだったでしょうか
答えはイテレータですね。
ここで print(next(it)) とすると
1 と表示
弾が1発、発射され、銃本体には4発残っています。
続けてprint(next(it))とすると
2 と表示
2発目の弾が発射され、銃本体には3発残っています。
続けてprint(next(it))とすると
3 と表示
3発目の弾が発射され、銃本体には2発残っています。
続けてprint(next(it))とすると
4 と表示
4発目の弾が発射され、銃本体には1発残っています。
続けてprint(next(it))とすると
5 と表示
5発目の弾が発射され、ここで弾切れになります。
続けてprint(next(it))とすると
StopIteration エラーになります
もう一度整理してみます。
イテレーション: 繰り返しのこと、ピストルのたとえだと、繰り返し撃てること
イテラブル :リストa、ピストルのたとえだと、弾倉
イテレータ :iter(a)、ピストルのたとえだと、銃本体
という関係になっています。以下は証拠のキャプチャ画像です。
まとめ
イテレータはなんのためにあるのか
→ 順番に1つずつ処理するため
どうやってつくるのか
→ iter()関数
なにをつくるのか
→ オブジェクト(イテレータオブジェクト)
最後になってエラーがでたとき
→ try exceptで処理
サンプル
a=iter([1,2,3]) #aはイテレータオブジェクト
b1=next(a) #1つずつ処理
print(b1)
b2=next(a)
print(b2)
b3=next(a)
print(b3)
try:
b4=next(a)
print(b4)
except StopIteration: #ここで例外処理
print("end")
後日談 2024/01/26追加
その後、ふとある疑問が....
iter()は関数である。関数であるからには状態を持てないはず
状態を持てないとイテラブルから値を1つずつ取り出すなんてできないはず
状態が持てないと、どこまで取り出したか忘れちゃうので
あれれ、いったいどうなっているのか???
クラスかクロージャを使っているんだろうと予想ができたのでchatGPTに聞いてみました
iter()はクラスですか、答えno
iter()はクロージャを使っているのか No
うーむ謎は深まるばかり。。。。。
この謎がわかったのは
イテラブル自体がオブジェクトだということ思い出したときです。たとえばar=[1,2,3]とすると、こいつはオブジェクトなのでメソッドをもてます。もちろん状態も持てる!! と書こうとおもってchatGPTに聞いたらそれも間違いだと言われました。
正解は
たとえば、for文が実施される前にイテレータオブジェクトというものが新たに作られます。イテレータオブジェクトは現在位置などの情報(プロパティ)を持ったクラスです。つまりイテラブルとは別のオブジェクト。イテレータオブジェクトがあるおかげで、たとえば、ar.next()を実行するたび、内部のプロパティが変化して、ひとつずつ値が取り出せるというわけなのでした。
まとめ
iter(ar)はar.__iter__()を呼ぶ関数,イテレータオブジェクトを作り出す
next(ar)はar.__next__()を呼ぶ関数,位置をずらす
といったことをやっています。
浅い理解で書いてしまったので、あわてて記事の前半を読み返したのですが、とりあえず間違ったことは書いてなさそうなので一安心しました。が、なにかまちがっていたらつっこみのほう優しくお願いします。