Pythonの「for」ループ(確定反復)

Pythonの「for」ループ(確定反復)

このチュートリアルでは、Pythonの `+ for +`ループで*確定反復*を実行する方法を示します。

この入門シリーズのhttps://realpython.com/python-while-loop/[前のチュートリアル]で、次のことを学びました。

  • 同じコードブロックを繰り返し実行することを「反復」と呼びます。

  • 反復には2つのタイプがあります。

  • Definite 反復。繰り返しの回数が事前に明示的に指定されます。

  • *無限*反復。コードブロックは、何らかの条件が満たされるまで実行されます

  • Pythonでは、「+ while +」ループを使用して無限の反復が実行されます。

このチュートリアルの内容は次のとおりです。

  • プログラミング言語で使用され、明確な反復を実装するいくつかの異なるパラダイムの比較から始めます。

  • 次に、 iterables および iterators について学習します。これらは、Pythonでの明確な反復の基礎を形成する2つの概念です。

  • 最後に、すべてをまとめて、Pythonの「+ for +」ループについて学習します。

*無料ボーナス:*リンク:[Python Tricks:The Book]の章にアクセスするには、ここをクリックして、Pythonのベストプラクティスを簡単な例とともに示します。すぐに適用して、より美しい+ Pythonコードを記述できます。

プログラミングにおける確定反復の調査

明確な繰り返しループは、多くの場合 + for + ループと呼ばれます。これは、 `+ for +`がPythonを含むほぼすべてのプログラミング言語で導入するために使用されるキーワードだからです。

歴史的に、プログラミング言語は、さまざまな種類の「+ for +」ループを提供してきました。 これらについては、次のセクションで簡単に説明します。

数値範囲ループ

最も基本的な「+ for +」ループは、開始値と終了値を持つ単純な数値範囲ステートメントです。 正確な形式は言語によって異なりますが、通常は次のようになります。

for i = 1 to 10
    <loop body>

ここでは、ループの本体が10回実行されます。 変数「+ i 」は、最初の反復で値「+1 +」、2回目の反復で「+2」などを想定します。 この種の「+ for +」ループは、BASIC、Algol、およびPascal言語で使用されます。

3つの式のループ

Cプログラミング言語で普及している別の形式の「+ for +」ループには、3つの部分が含まれています。

  • 初期化

  • 終了条件を指定する式

  • 各反復の終わりに実行されるアクション。

このタイプの形式は次のとおりです。

for (i = 1; i <= 10; i++)
    <loop body>

テクニカルノート: Cプログラミング言語では、 `+ i +`は変数 `+ i `をインクリメントします。 Pythonの ` i + = 1 +`とほぼ同じです。

このループは次のように解釈されます。

  • `+ i `を ` 1 +`に初期化します。

  • `+ i ⇐ 10 +`である限りループを続けます。

  • 各ループの反復後に、「+ i 」を「+1」ずつ増加させます。

3つの部分に指定された式はほとんど何でも使用できるため、3つの式の「+ for 」ループが一般的です。このため、上記の単純な数値範囲形式よりもかなり柔軟性があります。 これらの「 for 」ループは、C +、Java、PHP、およびPerl言語でも機能します。

コレクションベースまたはイテレータベースのループ

このタイプのループは、数値や条件を指定するのではなく、オブジェクトのコレクションを反復処理します。

for i in <collection>
    <loop body>

ループを通るたびに、変数「+ i 」は「 <collection> 」の次のオブジェクトの値を取ります。 このタイプの「 for 」ループは、おそらく最も一般化された抽象ループです。 PerlとPHPもこのタイプのループをサポートしていますが、「 for 」ではなく「 foreach +」というキーワードで導入されています。

さらに読む: For loop Wikipediaページで、プログラミング言語間の明確な反復の実装の詳細を確認してください。

Pythonの「+ for +」ループ

上記のループタイプのうち、Pythonが実装するのは最後のコレクションベースの反復のみです。 一見したところ、それは生の取引のように思えるかもしれませんが、Pythonの明確な反復の実装は非常に用途が広いため、だまされてしまうことはありません。

まもなく、Pythonの「+ for +」ループの根底を詳しく掘り下げます。 しかし今は、簡単なプロトタイプと例から始めましょう。

Pythonの `+ for +`ループは次のようになります。

for <var> in <iterable>:
    <statement(s)>

`+ <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 はオブジェクトを反復で使用できることを意味します。 この用語は次のように使用されます。

  • *形容詞:*オブジェクトは反復可能と表現できます。

  • *名詞:*オブジェクトは反復可能として特徴付けられます。

オブジェクトが反復可能な場合、組み込みのPython関数 `+ iter()+`に渡すことができます。この関数は iterator と呼ばれるものを返します。 はい、用語は少し繰り返されます。 そこにたむろしてください。 最終的にはすべてうまくいきます。

次の例の各オブジェクトは反復可能であり、 `+ iter()+`に渡されると、何らかのタイプの反復子を返します。

>>>

>>> iter('foobar')                             # String
<str_iterator object at 0x036E2750>

>>> iter(['foo', 'bar', 'baz'])                # List
<list_iterator object at 0x036E27D0>

>>> iter(('foo', 'bar', 'baz'))                # Tuple
<tuple_iterator object at 0x036E27F0>

>>> iter({'foo', 'bar', 'baz'})                # Set
<set_iterator object at 0x036DEA08>

>>> iter({'foo': 1, 'bar': 2, 'baz': 3})       # Dict
<dict_keyiterator object at 0x036DD990>

一方、これらのオブジェクトタイプは反復不可能です。

>>>

>>> iter(42)                                   # Integer
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    iter(42)
TypeError: 'int' object is not iterable

>>> iter(3.1)                                  # Float
Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    iter(3.1)
TypeError: 'float' object is not iterable

>>> iter(len)                                  # Built-in function
Traceback (most recent call last):
  File "<pyshell#28>", line 1, in <module>
    iter(len)
TypeError: 'builtin_function_or_method' object is not iterable

これまでに遭遇したコレクション型またはコンテナ型のすべてのデータ型は反復可能です。 これらには、https://realpython.com/python-strings/[string]、https://realpython.com/python-lists-tuples/#python-lists [list]、https://realpython.com/pythonが含まれます。 -lists-tuples/#python-tuples [tuple]、https://realpython.com/python-dicts/[dict]、https://realpython.com/python-sets/[set]、およびhttps://realpython.com/python-sets/#frozen-sets[frozenset]タイプ。

しかし、これらは反復できる唯一のタイプではありません。 Pythonに組み込まれている、またはモジュールで定義されている多くのオブジェクトは、反復可能になるように設計されています。 たとえば、Pythonで開いているファイルは反復可能です。 ファイルI/Oのチュートリアルですぐにわかるように、開いているファイルオブジェクトを反復処理すると、ファイルからデータが読み取られます。

実際、Pythonのほとんどすべてのオブジェクトを反復可能にすることができます。 ユーザー定義のオブジェクトでさえ、繰り返し処理できるように設計できます。 (これがどのように行われるかは、今後のオブジェクト指向プログラミングに関する記事で確認できます。)

イテレータ

さて、これでオブジェクトが反復可能であることの意味がわかり、 `+ iter()+`を使用してイテレータを取得する方法がわかりました。 イテレータを取得したら、それで何ができますか?

反復子は、本質的に、関連付けられた反復可能なオブジェクトから連続した値を生成する値プロデューサーです。 組み込み関数 `+ next()+`は、イテレータから次の値を取得するために使用されます。

上記と同じリストを使用した例を次に示します。

>>>

>>> a = ['foo', 'bar', 'baz']

>>> itr = iter(a)
>>> itr
<list_iterator object at 0x031EFD10>

>>> 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 "<pyshell#10>", line 1, in <module>
    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 +`の今後のチュートリアルでこれを行う方法を学習します。)無限のイテレーターからすべての値を一度に取得しようとすると、プログラムはhttps://en.wikipedia.org/wikiになります。/Hang_(computing)[hang]。

Python `+ for +`ループの根性

Pythonの `+ for +`ループがどのように機能するかを完全に理解するために必要なすべての概念を紹介しました。 先に進む前に、関連する用語を確認しましょう。

Term Meaning

Iteration

The process of looping through the objects or items in a collection

Iterable

An object (or the adjective used to describe an object) that can be iterated over

Iterator

The object that produces successive items or values from its associated iterable

iter()

The built-in function used to obtain an iterator from an iterable

ここで、このチュートリアルの最初に示した単純な「+ 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

Pythonのチュートリアルhttps://realpython.com/python-dicts/#built-in-dictionary-methods[dictionaries]で述べたように、辞書メソッド `+ .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()+`関数

このチュートリアルの最初のセクションでは、https://realpython.com/python-for-loop/#numeric-range-loop [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>)`は、 ` <end> `までで、 ` <end> `を含まない、 ` 0 +`で始まる整数を生成するイテラブルを返します。

>>>

>>> x = range(5)
>>> x
range(0, 5)
>>> type(x)
<class 'range'>

`+ 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> `までの整数を返すイテラブルを返します。 指定されている場合、 ` <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]

*テクニカルノート:*厳密に言えば、 `+ range()+`は組み込み関数ではありません。 不変のシーケンス型を作成する呼び出し可能なクラスとして実装されます。 しかし、実際的な目的では、組み込み関数のように動作します。

`+ range()`の詳細については、Real Pythonの記事https://realpython.com/python-range[Pythonの ` range()+`関数(ガイド)]を参照してください。

「+ for +」ループの動作を変更する

この入門シリーズの前のチュートリアルで、https://realpython.com/python-while-loop/#interruption-of-loop-iteration [+ break +`で `+ while +`ループの実行を中断する方法を説明しました。および `+ continue +`ステートメント]およびhttps://realpython.com/python-while-loop/#the-else-clause [+ else `句]で変更されます。 これらの機能は、 ` 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つのチュートリアルでは、ギアを少しシフトし、キーボードからの input およびコンソールへの output を介してPythonプログラムがユーザーと対話する方法を探ります。