Pythonの「for」ループ(確定反復)
このチュートリアルでは、Pythonのfor
ループを使用してdefinite iterationを実行する方法を示します。
この入門シリーズのprevious tutorialで、次のことを学びました。
-
同じコードブロックを何度も繰り返し実行することをiterationと呼びます。
-
反復には2つのタイプがあります。
-
Definite反復。反復回数は、事前に明示的に指定されます。
-
Indefiniteの反復。この反復では、何らかの条件が満たされるまでコードブロックが実行されます。
-
-
Pythonでは、
while
ループを使用して無期限の反復が実行されます。
このチュートリアルで取り上げる内容は次のとおりです。
-
プログラミング言語で使用され、明確な反復を実装するいくつかの異なるパラダイムの比較から始めます。
-
次に、iterablesとiteratorsについて学習します。これらは、Pythonでの明確な反復の基礎を形成する2つの概念です。
-
最後に、すべてを結び付けて、Pythonの
for
ループについて学習します。
Free Bonus:Click here to get access to a chapter from Python Tricks: The Bookは、Pythonのベストプラクティスと、より美しい+ Pythonicコードをすぐに適用できる簡単な例を示しています。
プログラミングにおける確定反復の調査
明確な反復ループは、for
がPythonを含むほぼすべてのプログラミング言語で導入するために使用されるキーワードであるため、しばしばfor
ループと呼ばれます。
歴史的に、プログラミング言語は、for
ループのいくつかのさまざまなフレーバーを提供してきました。 これらについては、次のセクションで簡単に説明します。
数値範囲ループ
最も基本的なfor
ループは、開始値と終了値を持つ単純な数値範囲ステートメントです。 正確な形式は言語によって異なりますが、通常は次のようになります。
for i = 1 to 10
ここでは、ループの本体が10回実行されます。 変数i
は、最初の反復で値1
、2番目の反復で値2
、というように仮定します。 この種のfor
ループは、BASIC、Algol、およびPascalの言語で使用されます。
3つの式のループ
Cプログラミング言語で普及しているfor
ループの別の形式には、次の3つの部分があります。
-
初期化
-
終了条件を指定する式
-
各反復の終わりに実行されるアクション。
このタイプの形式は次のとおりです。
for (i = 1; i <= 10; i++)
Technical Note: Cプログラミング言語では、i++
は変数i
をインクリメントします。 Pythonのi += 1
とほぼ同等です。
このループは次のように解釈されます。
-
i
を1
に初期化します。 -
i <= 10
までループを続けます。 -
各ループの反復後に
i
を1
ずつインクリメントします。
3つの部分に指定された式はほぼすべてであるため、3つの式のfor
ループが一般的です。したがって、これは、上記の単純な数値範囲形式よりもかなり柔軟性があります。 これらのfor
ループは、C ++、Java、PHP、およびPerl言語でも機能します。
コレクションベースまたはイテレータベースのループ
このタイプのループは、数値や条件を指定するのではなく、オブジェクトのコレクションを反復処理します。
for i in
ループを通過するたびに、変数i
は<collection>
の次のオブジェクトの値を取ります。 このタイプのfor
ループは、間違いなく最も一般的で抽象的なものです。 PerlとPHPもこのタイプのループをサポートしていますが、for
ではなくキーワードforeach
によって導入されています。
Further Reading:プログラミング言語間での明確な反復の実装の詳細については、For loopWikipediaページを参照してください。
Pythonfor
ループ
上記のループタイプのうち、Pythonが実装するのは最後のコレクションベースの反復のみです。 一見したところ、それは生の取引のように思えるかもしれませんが、Pythonの明確な反復の実装は非常に用途が広いため、だまされてしまうことはありません。
間もなく、Pythonのfor
ループの内臓を詳しく掘り下げます。 しかし今は、簡単なプロトタイプと例から始めましょう。
Pythonのfor
ループは次のようになります。
for in :
<iterable>
は、リストやタプルなどのオブジェクトのコレクションです。 ループ本体の<statement(s)>
は、すべてのPython制御構造と同様にインデントで示され、<iterable>
の項目ごとに1回実行されます。 ループ変数<var>
は、ループを通過するたびに<iterable>
の次の要素の値を取ります。
これが代表的な例です。
>>>
>>> a = ['foo', 'bar', 'baz']
>>> for i in a:
... print(i)
...
foo
bar
baz
この例では、<iterable>
はリストa
であり、<var>
は変数i
です。 ループを通過するたびに、i
はa
の連続する項目を引き受けるため、print()
は値'foo'
、'bar'
、および'baz'
を表示します。 、それぞれ。 このようなfor
ループは、反復可能でアイテムを処理するPythonの方法です。
しかし、反復可能なものとは正確には何ですか? for
ループをさらに調べる前に、Pythonのイテラブルが何であるかをより深く掘り下げることが有益です。
イテラブル
Pythonでは、iterableは、オブジェクトを反復で使用できることを意味します。 この用語は次のように使用されます。
-
An adjective:オブジェクトは反復可能として記述される場合があります。
-
A noun:オブジェクトは反復可能として特徴付けられる場合があります。
オブジェクトが反復可能である場合、それを組み込みのPython関数iter()
に渡すことができます。この関数は、iteratorと呼ばれるものを返します。 はい、用語は少し繰り返されます。 そこにたむろしてください。 最終的にはすべてうまくいきます。
次の例の各オブジェクトは反復可能であり、iter()
に渡されると何らかのタイプの反復子を返します。
>>>
>>> iter('foobar') # String
>>> iter(['foo', 'bar', 'baz']) # List
>>> iter(('foo', 'bar', 'baz')) # Tuple
>>> iter({'foo', 'bar', 'baz'}) # Set
>>> iter({'foo': 1, 'bar': 2, 'baz': 3}) # Dict
一方、これらのオブジェクトタイプは反復不可能です。
>>>
>>> iter(42) # Integer
Traceback (most recent call last):
File "", line 1, in
iter(42)
TypeError: 'int' object is not iterable
>>> iter(3.1) # Float
Traceback (most recent call last):
File "", line 1, in
iter(3.1)
TypeError: 'float' object is not iterable
>>> iter(len) # Built-in function
Traceback (most recent call last):
File "", line 1, in
iter(len)
TypeError: 'builtin_function_or_method' object is not iterable
しかし、これらは反復できる唯一のタイプではありません。 Pythonに組み込まれている、またはモジュールで定義されている多くのオブジェクトは、反復可能になるように設計されています。 たとえば、Pythonで開いているファイルは反復可能です。 ファイルI / Oのチュートリアルですぐにわかるように、開いているファイルオブジェクトを反復処理すると、ファイルからデータが読み取られます。
実際、Pythonのほとんどすべてのオブジェクトを反復可能にすることができます。 ユーザー定義のオブジェクトでさえ、繰り返し処理できるように設計できます。 (これがどのように行われるかは、今後のオブジェクト指向プログラミングに関する記事で確認できます。)
イテレータ
これで、オブジェクトが反復可能であるとはどういう意味かがわかり、iter()
を使用してオブジェクトから反復子を取得する方法がわかりました。 イテレータを取得したら、それで何ができますか?
反復子は、本質的に、関連付けられた反復可能なオブジェクトから連続した値を生成する値プロデューサーです。 組み込み関数next()
は、イテレータから次の値を取得するために使用されます。
上記と同じリストを使用した例を次に示します。
>>>
>>> a = ['foo', 'bar', 'baz']
>>> itr = iter(a)
>>> itr
>>> next(itr)
'foo'
>>> next(itr)
'bar'
>>> next(itr)
'baz'
この例では、a
は反復可能なリストであり、itr
は、iter()
で取得された関連する反復子です。 各next(itr)
呼び出しは、itr
から次の値を取得します。
イテレータが内部的に状態を保持する方法に注目してください。 どの値がすでに取得されているかを知っているので、next()
を呼び出すと、次に返す値がわかります。
イテレータの値がなくなるとどうなりますか? 上記のイテレータでもう1つnext()
呼び出しを行いましょう。
>>>
>>> next(itr)
Traceback (most recent call last):
File "", line 1, in
next(itr)
StopIteration
イテレータからのすべての値がすでに返されている場合、後続のnext()
呼び出しでStopIteration
例外が発生します。 イテレータから値を取得しようとすると失敗します。
イテレータから値を取得できるのは一方向のみです。 後戻りできません。 prev()
関数はありません。 ただし、同じ反復可能オブジェクトで2つの独立した反復子を定義できます。
>>>
>>> a
['foo', 'bar', 'baz']
>>> itr1 = iter(a)
>>> itr2 = iter(a)
>>> next(itr1)
'foo'
>>> next(itr1)
'bar'
>>> next(itr1)
'baz'
>>> next(itr2)
'foo'
イテレータitr1
がすでにリストの最後にある場合でも、itr2
はまだ最初にあります。 各イテレーターは、他のイテレーターから独立した独自の内部状態を維持します。
イテレータからすべての値を一度に取得する場合は、組み込みのlist()
関数を使用できます。 他の可能な使用法の中で、list()
はイテレータを引数として取り、イテレータが生成したすべての値で構成されるリストを返します。
>>>
>>> a = ['foo', 'bar', 'baz']
>>> itr = iter(a)
>>> list(itr)
['foo', 'bar', 'baz']
同様に、組み込みのtuple()
関数とset()
関数は、イテレータが生成するすべての値から、それぞれタプルとセットを返します。
>>>
>>> a = ['foo', 'bar', 'baz']
>>> itr = iter(a)
>>> tuple(itr)
('foo', 'bar', 'baz')
>>> itr = iter(a)
>>> set(itr)
{'baz', 'foo', 'bar'}
これを習慣にすることは必ずしも推奨されません。 イテレーターの優雅さの一部は、イテレーターが「遅延」していることです。つまり、イテレータを作成するときに、その時点で生成できるすべてのアイテムが生成されるわけではありません。 next()
で要求するまで待機します。 アイテムは、要求されるまで作成されません。
list()
、tuple()
などを使用する場合、イテレータにすべての値を一度に生成させるため、すべての値を返すことができます。 イテレータが返すオブジェクトの総数が非常に大きい場合、時間がかかる場合があります。
実際、Pythonで無限のオブジェクトのシリーズを返すイテレータを作成することは可能です。 (これを行う方法は、ジェネレーター関数とitertools
に関する今後のチュートリアルで学習します。)エンドレスイテレーターからすべての値を一度に取得しようとすると、プログラムはhangになります。
Pythonfor
ループの根性
これで、Pythonのfor
ループがどのように機能するかを完全に理解するために必要なすべての概念が紹介されました。 先に進む前に、関連する用語を確認しましょう。
Term | 意味 |
---|---|
繰り返し |
コレクション内のオブジェクトまたはアイテムをループするプロセス |
反復可能 |
反復できるオブジェクト(またはオブジェクトを説明するために使用される形容詞) |
イテレータ |
関連する反復可能オブジェクトから連続するアイテムまたは値を生成するオブジェクト |
|
イテレータからイテレータを取得するために使用される組み込み関数 |
ここで、このチュートリアルの最初に示した単純なfor
ループについてもう一度考えてみましょう。
>>>
>>> a = ['foo', 'bar', 'baz']
>>> for i in a:
... print(i)
...
foo
bar
baz
このループは、今学んだ概念の観点から完全に説明できます。 このfor
ループが記述する反復を実行するために、Pythonは次のことを行います。
-
iter()
を呼び出して、a
のイテレータを取得します -
next()
を繰り返し呼び出して、イテレータから各アイテムを順番に取得します -
next()
がStopIteration
例外を発生させたときに、ループを終了します
ループ本体は、アイテムnext()
が返されるたびに1回実行され、ループ変数i
は反復ごとに指定されたアイテムに設定されます。
この一連のイベントを次の図にまとめます。
おそらくこれは多くの不必要なモンキービジネスのように思えますが、そのメリットは相当なものです。 Pythonは、すべての反復可能要素に対するループをこの方法で処理します。Pythonでは、反復可能要素と反復子が多数あります。
-
多くの組み込みオブジェクトおよびライブラリオブジェクトは反復可能です。
-
反復可能オブジェクトを返す多くの関数を含む
itertools
と呼ばれる標準ライブラリモジュールがあります。 -
Pythonのオブジェクト指向機能で作成されたユーザー定義のオブジェクトは、反復可能にすることができます。
-
Pythonには、ジェネレータと呼ばれる構造があり、これを使用すると、単純で簡単な方法で独自のイテレータを作成できます。
このシリーズ全体を通して、上記のすべてについて詳しく知ることができます。 それらはすべてfor
ループのターゲットになる可能性があり、構文は全体的に同じです。 シンプルでエレガントで非常に多用途です。
辞書を反復処理する
イテレータはiter()
のディクショナリから取得できることを以前に見たので、ディクショナリはイテレータでなければならないことがわかります。 辞書をループするとどうなりますか? どれどれ:
>>>
>>> d = {'foo': 1, 'bar': 2, 'baz': 3}
>>> for k in d:
... print(k)
...
foo
bar
baz
ご覧のとおり、for
ループがディクショナリを反復処理すると、ループ変数がディクショナリのキーに割り当てられます。
ループ内で辞書の値にアクセスするには、通常どおりキーを使用して辞書参照を作成できます。
>>>
>>> for k in d:
... print(d[k])
...
1
2
3
.values()
を使用して、辞書の値を直接反復することもできます。
>>>
>>> for v in d.values():
... print(v)
...
1
2
3
実際、辞書のキーと値の両方を同時に反復処理できます。 これは、for
ループのループ変数が単一の変数だけに限定されていないためです。 また、タプルにすることもできます。この場合、割り当てステートメントの場合と同様に、パッキングとアンパックを使用してiterableのアイテムから割り当てが行われます。
>>>
>>> i, j = (1, 2)
>>> print(i, j)
1 2
>>> for i, j in [(1, 2), (3, 4), (5, 6)]:
... print(i, j)
...
1 2
3 4
5 6
Pythondictionariesのチュートリアルで説明したように、ディクショナリメソッド.items()
は、キーと値のペアのリストをタプルとして効果的に返します。
>>>
>>> d = {'foo': 1, 'bar': 2, 'baz': 3}
>>> d.items()
dict_items([('foo', 1), ('bar', 2), ('baz', 3)])
したがって、キーと値の両方にアクセスする辞書を反復処理するPythonの方法は次のようになります。
>>>
>>> d = {'foo': 1, 'bar': 2, 'baz': 3}
>>> for k, v in d.items():
... print('k =', k, ', v =', v)
...
k = foo , v = 1
k = bar , v = 2
k = baz , v = 3
range()
関数
このチュートリアルの最初のセクションでは、numeric range loopと呼ばれるタイプのfor
ループを確認しました。このループでは、開始数値と終了数値が指定されています。 この形式のfor
ループはPythonに直接組み込まれていませんが、簡単にアクセスできます。
たとえば、0
から4
までの値を反復処理する場合は、次のようにするだけです。
>>>
>>> for n in (0, 1, 2, 3, 4):
... print(n)
...
0
1
2
3
4
このソリューションは、数が少ない場合でもそれほど悪くありません。 しかし、番号の範囲がはるかに大きい場合、すぐに退屈になります。
幸いなことに、Pythonには、より優れたオプションである組み込みのrange()
関数が用意されています。この関数は、整数のシーケンスを生成する反復可能オブジェクトを返します。
range(<end>)
は、0
で始まり、<end>
までの整数を生成するイテラブルを返します。
>>>
>>> x = range(5)
>>> x
range(0, 5)
>>> type(x)
range()
は、値のリストまたはタプルではなく、クラスrange
のオブジェクトを返すことに注意してください。 range
オブジェクトは反復可能であるため、for
ループを使用して値を反復処理することにより、値を取得できます。
>>>
>>> for n in x:
... print(n)
...
0
1
2
3
4
list()
またはtuple()
を使用して、すべての値を一度に取得することもできます。 REPLセッションでは、値が何であるかをすばやく表示する便利な方法になります。
>>>
>>> list(x)
[0, 1, 2, 3, 4]
>>> tuple(x)
(0, 1, 2, 3, 4)
ただし、大規模なアプリケーションの一部であるコードでrange()
を使用する場合、通常、この方法でlist()
またはtuple()
を使用することは不適切な方法と見なされます。 イテレータと同様に、range
オブジェクトはレイジーです。指定された範囲の値は、要求されるまで生成されません。 range
オブジェクトでlist()
またはtuple()
を使用すると、すべての値が一度に返されます。 これが必要になることはめったにありません。リストが長い場合、時間とメモリを浪費する可能性があります。
range(<begin>, <end>, <stride>)
は、<begin>
で始まり、<end>
までの整数を生成するiterableを返します。 指定した場合、<stride>
は、値の間でスキップする量を示します(文字列とリストのスライスに使用されるストライド値に類似しています)。
>>>
>>> list(range(5, 20, 3))
[5, 8, 11, 14, 17]
<stride>
を省略すると、デフォルトで1
になります。
>>>
>>> list(range(5, 10, 1))
[5, 6, 7, 8, 9]
>>> list(range(5, 10))
[5, 6, 7, 8, 9]
range()
に指定するすべてのパラメーターは整数である必要がありますが、いずれも負の値にすることができます。 当然、<begin>
が<end>
より大きい場合、<stride>
は負である必要があります(結果が必要な場合)。
>>>
>>> list(range(-5, 5))
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
>>> list(range(5, -5))
[]
>>> list(range(5, -5, -1))
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]
Technical Note:厳密に言えば、range()
は厳密には組み込み関数ではありません。 不変のシーケンス型を作成する呼び出し可能なクラスとして実装されます。 しかし、実際的な目的では、組み込み関数のように動作します。
range()
の詳細については、Real Pythonの記事Python’s range()
Function (Guide)を参照してください。
for
のループ動作の変更
この入門シリーズの前のチュートリアルで、while
ループの実行をbreak
and continue
statementsで中断し、else
clauseで変更する方法を説明しました。 これらの機能は、for
ループでも使用できます。
break
およびcontinue
ステートメント
break
およびcontinue
は、while
ループと同じようにfor
ループで機能します。 break
はループを完全に終了し、ループに続く最初のステートメントに進みます。
>>>
>>> for i in ['foo', 'bar', 'baz', 'qux']:
... if 'b' in i:
... break
... print(i)
...
foo
continue
は現在の反復を終了し、次の反復に進みます。
>>>
>>> for i in ['foo', 'bar', 'baz', 'qux']:
... if 'b' in i:
... continue
... print(i)
...
foo
qux
else
句
for
ループには、else
句を含めることもできます。 解釈は、while
ループの解釈に類似しています。 else
句は、反復可能オブジェクトの枯渇によってループが終了した場合に実行されます。
>>>
>>> for i in ['foo', 'bar', 'baz', 'qux']:
... print(i)
... else:
... print('Done.') # Will execute
...
foo
bar
baz
qux
Done.
リストがbreak
ステートメントで分割されている場合、else
句は実行されません。
>>>
>>> for i in ['foo', 'bar', 'baz', 'qux']:
... if i == 'bar':
... break
... print(i)
... else:
... print('Done.') # Will not execute
...
foo
結論
このチュートリアルでは、Pythonのdefinite iterationの主力製品であるfor
ループを紹介しました。
また、iterablesとiteratorsの内部動作についても学びました。これは、明確な反復の根底にある2つの重要なオブジェクトタイプですが、他のさまざまなPythonコードでも顕著に表れています。
この入門シリーズの次の2つのチュートリアルでは、ギアを少しシフトし、Pythonプログラムがキーボードからinputおよびコンソールへのoutputを介してユーザーと対話する方法を探ります。