Skip to content
This page has been auto-translated and may contain errors.View in English

制御フロー

docs.scrimba.com

これまで書いたプログラムは毎回同じように動作します。上から下へ、1行ずつ実行されます。単純なスクリプトではうまくいきますが、現実のプログラムは判断を下して処理を繰り返す必要があります。クイズは答えが正しいかをチェックし、ゲームはプレイヤーが勝つか負けるかまで実行し続ける必要があります。この章では、プログラムを分岐させて繰り返す方法を学びます。

制御フローはプログラムが進む経路を形作ります。条件文(if/elif/else)は分岐を選び、ループ(whilefor)はブロックを繰り返します。Pythonのforはインデックスを数えるのではなく、1つずつアイテムを歩いていくため、多くのものをきれいにループします。この章では、各ツールをいつ使うべきか、そして各ツールが招く間違いについて学びます。

Pythonは3つの制御フロー ツールを提供します: 分岐にはif/elif/else、繰り返しにはwhileforです。forループはイテラブル(リストやファイルのように1つずつステップできるもの)を歩き、アイテムがなくなると自動的に停止するため、インデックスを手で管理することはほぼありません。whileループはその条件が真ままであり続ける限り続きます。breakcontinueは、周囲の最も近いループだけに影響し、ループのelse句(後で説明)はbreakなしでループが終了した場合にのみ実行されます。この章の残りは、正しいものを選んで各ツールが招く失敗パターンを避けることについてです。

比較

決断を下す前に、物事を比較する必要があります。比較演算子はTrueまたはFalseを返します。最初に正しく理解することが重要な点: =は値を代入し、==は2つの値が等しいかをチェックします。これを混同することは、初心者が最もよく犯す間違いの1つです。

比較演算子はboolを返し、Pythonではそれらをチェーンできます: 0 < x < 100 < x and x < 10を意味し、ほかの言語は左から右に読んで誤ります。文字列は辞書順で文字ごとに比較されるため、大文字は小文字の前にソートされます。値を比較するには==を使い、isをほぼ使う必要はありません(Deep Diveで説明)。

比較はboolを返し、実際のコードでは2つの詳細が重要です。まず、Pythonはチェーンされた比較をサポートします: a < b < ca < b and b < cとして読まれ、中間の値bは1回だけ評価されます。これは、それが高コストな呼び出しの場合、知る価値があります。次に、==は2つの値が等しいかをチェックし、isアイデンティティ(2つの名前がメモリ内の全く同じオブジェクトを指しているか)をチェックします。これらは相互交換可能ではありません: 値を比較するには==を使い、NoneTrueFalseの場合にのみisを保持します。ここで実際に唯一のオブジェクトが必要です。文字列や数字でisに手を出すと、小さな入力ではパスするが大きな入力では失敗するバグが生じます。

python
5 > 3     # True
5 < 3     # False
5 == 5    # True   (注: ダブルイコール; = は代入、== は比較)
5 != 3    # True   ("以上でない")
5 >= 5    # True   ("以上")
5 <= 4    # False  ("以下")

===の区別はほぼ誰もが初期段階でつまずきます。代入(=)は値を保存し、比較(==)は2つの値が同じかをチェックします。

文字列も比較できます。Pythonはそれらをアルファベット順に、文字ごとに比較します。文字列の章で詳しく説明します:

python
"apple" == "apple"   # True
"apple" < "banana"   # True  (a は b の前)
"apple" == "Apple"   # False (大文字小文字を区別)
Juno比較= は値を保存し、== は2つの値が等しいかをチェックします。疲れているときは今でも立ち止まってダブルチェックするので、つまずいても悪くありません。比較はTrueまたはFalseを渡し、文字列でも機能します。
Juno比較 比較はboolを返します。0 < x < 10のようなチェーン形は0 < x and x < 10として読まれ、ほかの言語はそれを誤ります。文字列の比較はUnicodeの順序によるため、大文字小文字を区別してソートされます。
Juno比較 値には==を、アイデンティティにはisを使い、isNoneTrueFalseに保持します。罠は文字列や整数でのisです: 小さな入力ではパスし、大きな入力では失敗します。チェーンされた比較は中間項を1回だけ評価するため、それが高コストの場合に便利です。

条件を組み合わせる

andornotは比較を組み合わせます。andは両側が真である必要があります。orは少なくとも一方が真である必要があります。notは結果を反転させます。これらにより、「スコアは合格かつユーザーはアクティブ」のような現実世界の条件を表現できます。

andorはショートサーキット動作をします: andは最初の偽の値で停止し、orは最初の真の値で停止します。これらは裸のTrueまたはFalseではなく、その実際のオペランドを返すため、name or "guest"はフォールバックを提供する清潔な方法です。notは常に単純なboolを返します。

andorTrueまたはFalseを返さず、オペランドの1つを返します。andは最初の偽のオペランド、またはすべて真の場合は最後のオペランドを返します。orは最初の真のオペランド、またはすべて偽の場合は最後を返します。左側が結果を決めた後、右側は評価されず、その**ショートサーキット**保証(Pythonは答えが分かったら停止)は何かに構築するものです: user and user.nameuserが存在するときにのみuser.nameを読むため、欠落した値でエラーが発生しません。反面は現実的な失敗パターンです: 右側に数えている副作用(ログや書き込みの関数など)がある場合、左側がショートサーキットしたときに静かに実行されません。

python
age = 25
score = 88

age >= 18 and score >= 80    # True  (両方とも真である必要があります)
age < 18 or score >= 80      # True  (少なくとも1つは真である必要があります)
not age >= 18                # False (結果を反転させます)

andは両側を必要とします。orは少なくとも一方を必要とします。notは反転させます。

Juno条件を組み合わせるand は両側が真を望み、or は少なくとも1つが真を望み、not は答えを反転させます。これはあなたが書く最も現実的な条件、例えば「スコアは合格andユーザーはアクティブ」をカバーします。Pythonは結果が分かった瞬間に確認を停止します。
Juno条件を組み合わせるand は最初の偽の値で停止し、or は最初の真の値で停止し、どちらも裸のTrue/Falseではなく、そのオペランドを返します。そのショートサーキットはx and x.method()xが空のときに安全なままである理由です。
Juno条件を組み合わせるand/or はオペランドを返し、boolではなく、左側が結果を決めた後、右側はスキップされます。user and user.nameのようなガードにそれを頼りにします。別のエッジに注意します: 右側に副作用がある場合、ショートサーキットしたときに静かに実行されません。

真と偽

Pythonのすべての値には真偽の解釈があり、TrueまたはFalseでなくても。空の文字列、ゼロ、空のリスト、およびNoneはすべて条件でFalseのように振る舞います。その他すべてはTrueのように振る舞います。これはif results:がリストが非空であるかをチェックif len(results) > 0:と書くことなくチェックできることを意味します。

Pythonの偽の値はFalse00.0""[](){}set()、およびNoneです。その他すべては真です。つまり、空のコンテナは偽であり、非空のものは真です。そのため、if results:は慣例的な空のチェックです: if len(results) > 0:の代わりに書きます。

条件にオブジェクトを入れたとき、Pythonは__bool__メソッドを呼び出して(ダンダー、Pythonが背後で呼び出すダブルアンダースコアメソッドの1つ)、はい/いいえの答えを要求します。型が__bool__を定義しない場合、Pythonは__len__にフォールバックするため、長さがゼロのものはすべて偽です。標準的な偽の値はFalse00.0""、空のコンテナ([](){}set())、およびNoneです。その他すべては真です。注意すべき罠: if results:は空のリストでもNoneでも偽であるため、「空」と「まだ設定されていない」を区別する必要がある場合は、真偽性に依存するのではなく、if results is None:をテストします。

python
# これらはすべて条件で False のように振る舞います:
False, 0, 0.0, "", [], {}, (), None

# その他すべては True のように振る舞います

これはif results:が「リストに何かあれば」という自然な言い方であり、if name:は文字列にコンテンツがあるかをチェックすることを意味します。

Juno真と偽 空のものとゼロは条件でFalseとして数えられます: 0""[]、およびNone。その他すべてがTrueとして数えられます。そのためif results:は「リストに何かあれば」として読むので、len()は必要ありません。
Juno真と偽 偽はFalse、ゼロ、空の文字列、空のコンテナ、およびNoneです。その他すべては真です。つまり、空のリストは偽であり、満杯のリストは真です。これがif my_list:が空のチェックとして機能する理由全体であり、len()は必要ありません。
Juno真と偽 条件は__bool__を呼び出し、その後__len__が欠落している場合はそれを呼び出すため、自分のクラスは自分の真偽性を選択できます。キャッチは: if results:は空のリストをNoneから区別できません。その区別が重要なとき、代わりにis Noneに頼ります。

if / elif / else

if文はその条件がTrueのときだけコードのブロックを実行します。elifは最初の条件が偽だった場合にチェックする条件を追加します。elseは条件にマッチしなかったすべてをキャッチします。Pythonは中括弧ではなく、インデントを使用して各ブロック内に何が属しているかを定義します。

if/elif/elseは条件を上から下に評価し、マッチする最初のブロックを実行します。Pythonはインデント(慣例では4つのスペース)を使用してブロック スコープを定義します。一貫性のないインデントはSyntaxErrorです。1つのブランチだけが実行されます: 条件がマッチしたら、すべての後続elifelseはスキップされます。

Pythonは条件を上から下に確認し、1つのブランチを実行します: 条件が真である最初のブランチ、またはマッチするものがない場合はelse。各条件は遅延評価されます。つまり、Pythonは上の条件がすべて偽の場合にのみテストするため、順序はロジックの一部です。安いまたは最も可能性の高いテストを最初に入れ、重なった範囲を順序付けして、より厳しい境界がより緩い境界の前に来るようにします。そうしないと、後のブランチが到達不可能になります。インデントはここでブロック構造であり、スタイルの選択ではないため、タブとスペースを混在させると、静かな意味の変化ではなく、TabErrorになります。

python
score = 87

if score >= 90:
    print("A grade")
elif score >= 80:
    print("B grade")
elif score >= 70:
    print("C grade")
else:
    print("Below C")

ルール:

  • ifは必須で常に最初に来ます
  • elif(「else if」の短縮)はオプションで、必要な数だけ持つことができます
  • elseはオプションで、マッチしなかったすべてを処理し、最後に来ます
  • Pythonはインデント(4つのスペース)を使用して、各ブロック内に何が属しているかをマークします。中括弧はありません

インデントはオプションでも美的でもありません。Pythonはそれを構造を定義するために使用します。インデントの一貫性がないことは構文エラーです。

Junoif / elif / elseif は条件が真のときそのブロックを実行し、elif はより多くのケースをチェックし、else は残りをキャッチします。最初のマッチだけが実行されます。私が初期段階でハマった点: インデントはPythonに各ブロックがどこで始まり、どこで終わるかを伝えるため、一貫性を保ちます。
Junoif / elif / else 条件は上から下にチェックされ、最初のマッチだけが実行されるため、順序が重要です: より狭い範囲をより広い範囲の前に入れます。インデント(4つのスペース)はブロックをマークし、それを台無しにすることはSyntaxErrorであり、静かなバグではありません。
Junoif / elif / else 条件は遅延評価され、上から下に評価されるため、安いまたは最も可能性の高いテストを最初に入れ、重なった境界を厳しい前で順序付けします。そうしないと、ブランチが無効になります。インデントは構造であり、スタイルではなく、タブ対スペースは静かな変化ではなくTabErrorです。

ワンライン条件

単純な yes/no 代入の場合、Pythonは三項式と呼ばれるコンパクトなワンラインの形式を持っています: value_if_true if condition else value_if_false。ロジックが小さく、文のように読めるときにのみ使用します。

条件式(三項演算子)は条件に基づいて2つの値のいずれかに評価されます。これはステートメントではなく式であるため、値が期待される場所ならどこでも表示できます: f文字列内、関数の引数として、代入内で。シンプルな yes/no ケースに使用します。elifを含むものについては、完全なバージョンを書きます。

条件式x if condition else yは条件を評価し、もう一方を実行しないで片側を返すため、そうでなければエラーが発生する場所で安全に使用できます。これは式であるため、値が行く場所ならどこでもスロットを入れます: f文字列、関数の引数、デフォルト。規律は停止時期を知ることです。elifを保持することはできず、チェーンすること(a if p else b if q else c)は置き換えられるブロックより悪く読めるため、決定が1つ以上ある場合、完全なif/elif/elseに頼ります。後でこれを保持する読者は感謝してくれるでしょう。

python
label = "pass" if score >= 50 else "fail"

これは三項式です。文のように読みます。ロジックが小さいときに使用します。elifを含むものについては、完全なバージョンを書きます。

Junoワンライン条件value_if_true if condition else value_if_false は1行で2つの値のいずれかを選択します。"pass" if score >= 50 else "fail"のようなきちんとした小さな選択に最適です。ロジックがそれを超えて成長した瞬間、通常のifブロックに戻ります。より良く読めるでしょう。
Junoワンライン条件 三項式は式であるため、値が行く場所ならどこでもフィットします: f文字列、引数、代入です。シンプルな2方向の選択に使用します。elifを持つ何かは完全なブロックに属します。
Junoワンライン条件x if cond else y は選択した側だけを実行するため、1つのブランチが発生する場所で安全であり、あらゆる式スロットにドロップします。チェーンしないでください: スタックされた三項式は置き換えられるブロックより悪く読めるため、決定の過去の1つで、それを書き出します。

while ループ

whileループはその条件がTrueである限り、ブロックを繰り返します。ループが何回実行されるべきかが事前に分からない場合に使用します。例えば、有効な入力を待つか、ジョブが成功するまで再試行する場合。

whileは各反復の前にその条件を評価し、条件が真のときだけブロックを実行します。ループの終了条件がループ内で変わるもの(何かに基づいている場合)に使用します。反復回数が既知の場合、またはコレクションを反復する場合、forは通常よりきれいです。

whileは毎回その条件を再テストし、ボディはその条件を変えるため、失敗パターンは構造的です: 終了に向かって移動することを忘れる(カウンター をデクリメント、キューを消費)し、終わらないループを取得して、プロセスがハングします。終了がボディの途中または終わりで決定される場合、慣例的な形はwhile Trueと内部breakであり、ヘッダーで強制するのではなく、実際に発生する場所に条件を保持します。コレクションを歩くか、既知の回数を実行する場合、forループはよりきれいで、カウンターのオフバイワンの機会を削除します。

python
lives = 3

while lives > 0:
    print(f"Lives remaining: {lives}")
    lives -= 1

print("Game over")

whileは、ループが何回実行されるかが事前に分からない場合に最適です。既知の場合、またはコレクションを反復する場合、forはよりきれいです。

Junowhile ループwhile は条件が真ままである限り、そのブロックを実行し続けます。有効な入力を待つのような、何ラウンド必要かが分からない場合にそれに頼ります。何かがループ内で終了に向かって移動することを確認します。そうしないと、永遠に実行され、1回以上犯した間違いです。
Junowhile ループwhile は各パスの前にその条件をチェックし、ボディはそれを変えます。終了がループ内で発生することに依存する場合に使用します。数えているか、コレクションを歩く場合、forはよりきれいなピックです。
Junowhile ループ ボディは終了に向かって移動する必要があります。そうしないとハングが発生するため、チェックする最初のもの。while Trueと内部breakは、終了がボディの真ん中に着地する場合の清潔な形。既知のカウントまたはコレクションの場合、forを優先し、オフバイワンリスクをスキップします。

break と continue

breakはループを即座に終了し、残りの反復がいくつあっても構いません。continueは現在の反復の残りをスキップし、条件チェックに戻ります。両方とも、それらが内部にある最も内側のループにのみ影響します。

breakは最も近いエンクロージングループを終了し、その後の最初のステートメントに制御を転送します。continueはループボディの残りをスキップし、条件チェック(またはforループの次の反復)から再開します。両方とも最も近いエンクロージング ループにのみ影響します。

breakはループを即座に離れ、そのelse句をスキップします(ある場合)。continueは次のパスについてループヘッダーに直接ジャンプします。両方は最も近いエンクロージング ループにバインドし、Pythonはラベル付きbreakを持たないため、内部のものから外部ループに到達することはできません。一度に複数のレベルを脱出する必要がある場合、きれいな動きは、ネストされたループを関数に引き上げてreturnすることです。これはフラグ変数をスレッド化するより読みやすくなります。フラグは動作しますが、欠落しているラベルを回避するために追加する追加のif found:は、将来の編集が間違える可能性のある場所です。

breakはループを即座に終了します:

python
target = 5
num = 0

while True:
    num += 1
    if num == target:
        print(f"Found {target}")
        break   # ループを停止

while True:breakは、終了条件が複雑であるか、ループボディの終わりで発生する必要がある場合、有効で一般的なパターンです。

continueは現在の反復の残りをスキップし、条件チェックに戻ります:

python
num = 0

while num < 10:
    num += 1
    if num % 2 == 0:
        continue    # 偶数をスキップ
    print(num)      # 奇数だけ印刷: 1, 3, 5, 7, 9
Junobreak と continuebreak はループから即座にジャンプアウト、continue はこのラウンドの残りをスキップして条件チェックに戻ります。両方はそれらが直接内部にあるループだけに触れます。while True:breakは最初は奇妙に読みますが、何かが発生するまでループするための通常の方法です。
Junobreak と continuebreak は最も近いループを離れ、continue は条件から再開します。どちらも外部ループに到達しません。ネストされたループから脱出するには、フラグを設定するか、内部ループを関数に引き上げてreturnします。
Junobreak と continue 両方は最も近いループにバインドし、ラベル付きbreakはないため、複数のレベルを脱出することは、フラグまたはよりきれいに、returnを持つ関数を意味します。breakはループのelseもスキップします。これは検索パターンが頼りにすることです。

for ループ

forループはシーケンスを1つずつ、1つのアイテムずつ通り抜けます: リスト、文字列、数値の範囲。forの後で名前を付けた変数は順番に各アイテムを受け取ります。カウンターを管理したり、長さ自分でチェックしたりする必要はありません。

forは与えられたもので1つずつアイテムを要求し、アイテムがなくなると停止します。インデックス位置を数えるのではなく。つまり、リスト以上に多くで機能します: 文字列、辞書、範囲、ファイルオブジェクトはすべて同じ方法でループし、長さチェックやカウンター側はありません。コレクションを歩く場合はwhileよりforに頼ります。

forはインデックスを数えず、与えられたものにイテレータ(一度に1つのアイテムを配ってそれが停止した場所を記憶するオブジェクト)をそれに要求し、なくなるまでアイテムを引き出します。実際の利益はforがリスト以上に多く で機能することです: 文字列、辞書、範囲、ファイルオブジェクト、および遅延ソースのようなジェネレータ(要求時に計算されるシーケンス、メモリに保持されない)はすべて同じ方法でループします。その最後のグループは1つの落とし穴を生きています: 遅延イテレータはシングルユースなため、forループがジェネレータまたはファイルを排出した後、それを再度ループするとなにも取得しません。アイテムが必要な場合は2回、まずそれらをリストにマテリアライズするか、ソースを再度開きます。

python
players = ["太郎", "花子", "一郎"]

for player in players:
    print(f"Hello, {player}!")

forループは文字列でも機能します(文字ごとに反復)し、その他のシーケンスタイプでも機能します。

Junofor ループfor item in sequence は順番に各アイテムを歩き、カウンターや長さ自分に触れることはありません。リスト、文字列、範囲、たくさんのもので機能します。他の言語から来て、インデックスを管理しないことは最初は奇妙に感じました。その後、ギフトのように感じました。
Junofor ループfor は一度に1つのアイテムを引き出し、なくなると停止するため、インデックスシーケンスにバインドされません。リスト、文字列、dict、範囲、ファイル: すべてのための同じループ。インデックスの簿記なし、長さチェックなし。
Junofor ループfor 任意のイテレータを駆動するため、リスト、ファイル、ジェネレータはすべて同じようにループします。キャッチは遅延ものです: ジェネレータまたはファイル イテレータはシングルユースであり、1回のパスの後に排出されます。アイテムが必要なら2回? まずそれらをリストに引き出すか、ソースを再度開きます。

range()

range()は数値のシーケンスを生成し、ループします。range(5)0、1、2、3、4を与えます。開始、終了、およびステップ サイズを制御できます。ループが特定の回数実行される必要がある場合に使用します。

range(start, stop, step)startから(含まない)stopまでの整数を作成し、stepによってステップします。これは遅延シーケンスです: リストを作成しません。要求に応じて数値を生成します。これはrange(10_000_000)をメモリ効率的にします。3つのフォーム すべてが逆数え用の負の引数を受け入れます。

rangeは数値のリストを作成しません。開始、停止、ステップだけを保存し、それが進むにつれて各値を計算します。これはrange(10_000_000)range(10)と同じメモリコストすることを意味するため、反復する必要がある場合は、list()でラップしない直接rangeを反復し、1000万個の保存された整数の支払いを避けます。それはシーケンスが数えられる場所で振る舞う: あなたはそれにインデックスを付け、それをスライスし、len()を要求し、すべて安くinでメンバーシップをテストできます。list(range(n))を呼び出すことは、実際に保持する本当の、再利用可能なリストが実際に必要な場合にのみ価値があります。

python
for i in range(5):
    print(i)    # 0, 1, 2, 3, 4

range()には3つのフォームがあります:

呼び出し何を生成するか
range(5)0, 1, 2, 3, 4
range(2, 6)2, 3, 4, 5
range(0, 10, 2)0, 2, 4, 6, 8 (ステップ 2)
range(5, 0, -1)5, 4, 3, 2, 1 (カウント ダウン)

range()はリストを作成しません。一度に1つの数値を生成します。これは非常に大きな範囲でも効率的です。

Junorange()range(5)0, 1, 2, 3, 4を与え、開始、停止、ステップを設定できます。ループが一定の回数実行される場合にそれに頼ります。リストを作成しません。各ラウンドに1つの数値を配ります。
Junorange()range(start, stop, step)startからstopを含まない最大までカウントします。これは遅延シーケンスであるため、リストを作成するのではなく、要求に応じて数値を生成します。これがrange(10_000_000)がほぼ何もコストしない理由です。負のステップはカウント ダウンです。
Junorange()range は開始、停止、ステップだけを保存するため、どのくらい大きくても平坦なメモリです。本当に再利用可能なリストが必要な場合を除き、`list(range(n))`の代わりに直接それを反復します。それはまだインデックス、スライス、`in`と`len()`をサポートします。安く。

enumerate()

enumerate()はループしながらインデックスと値の両方を与えるため、別々にカウンターを追跡する必要はありません。i, playerの部分は各反復に自動的に値のペアを受け取ります。

enumerate(iterable, start=0)は任意のイテレータをラップして(index, value)タプルを生成します。startパラメータはカウンターをオフセットしますが、基本的なインデックスは変更しません。カウンター変数を管理するのよりenumerate()を優先します。それはよりきれいで、エラーが少ないです。

enumerateはイテラブルをラップし、(count, value)ペアを配ります。forヘッダーはアンパック(Python1ステップでタプルの各部分を独自の名前に代入)を通じて2つの名前に分割します。これは遅延で、カウンター以外は何も追加しないため、裸のループと同じコストです。手で管理されたindex = 0; index += 1より常にそれに頼る理由は信頼性です: 手動バージョンは、インクリメントを忘れるか、間違った場所でバンプする場所のバグを招きます。enumerateはその間違いのクラス全体を削除します。人間が数えるのは1から0ではなく、出力を番号付けするときにstart=1を渡します。

python
players = ["太郎", "花子", "一郎"]

for i, player in enumerate(players):
    print(f"{i + 1}. {player}")
# 1. 太郎
# 2. 花子
# 3. 一郎

i, player構文はアンパックと呼ばれます。Python は(index, value)ペアを2つの名前に自動的に分割します。

デフォルトではenumerate()は0で開始します。開始値を渡して変更します:

python
for i, player in enumerate(players, start=1):
    print(f"{i}. {player}")    # 1から開始
Junoenumerate()enumerate() は位置と値を一緒に与えるため、同期を保つ個別カウンターはありません。i, playerの部分は一度に両方を引っ掛けます。誰かがリストを読むために番号を付けている場合、`start=1`を渡します。
Junoenumerate()enumerate(iterable, start=0)は`for`ヘッダーで直接アンパックされる`(index, value)`ペアを生成します。カウンター変数に優先します。それはよりきれいで、インクリメントするのを忘れることはできません。`start`は表示された数だけをシフトします。実際のインデックスではなく。
Junoenumerate() 遅延、カウンター以上の無料、そして整体を「インクリメントするのを忘れた」バグ手動インデックスに招待する全体削除します。デフォルトに頼ります。各アイテムはタプルであるため、`for i, v`アンパックが機能するのはそのためです。`start=1`人間に直面した番号付けの場合。

ネストされたループ

別のループ内にループを配置できます。内部ループは外部ループの単一の反復ごとに完全に実行されます。これはグリッド、組み合わせ、または2つのレベルの構造を持つデータを処理する方法です。

ネストされたループは、外側の長さm、内側の長さnのO(m × n)反復数を持ちます。ネストされたループ内のbreakcontinueは最も内側のループにのみ影響します。複数のレベルから脱出するには、フラグ変数を使用するか、関数に再構築します。

ネストされたループは乗算します: 長さnの内部周りの長さmの外部ループは、ボディをm倍n倍実行するため、コストは急速に上昇し、大きな入力の一対のループが遅いスクリプトの背後にある通常の犯人です。3番目のレベルに頼る前に、データ構造が本当の修正であるかを尋ねます(辞書ルックアップは内部検索ループをコラボできます)。2つのシーケンスのすべての組み合わせが必要な場合(カルテシア積、1つ以上の他のペアリング)、itertools.product標準ライブラリから読むのはネストより良くなります。また、breakは内部ループだけを離れることを思い出してください: 両方を離れるには、それらを関数に引き上げてreturnします。

python
rows = [1, 2, 3]
cols = ["A", "B"]

for row in rows:
    for col in cols:
        print(f"{col}{row}", end=" ")
    print()   # 各行の後の新しい行
# A1 B1
# A2 B2
# A3 B3

ネストされたループ内のbreakcontinueは最も内側のループにのみ影響します。

Junoネストされたループ 別のループ内のループはすべての外部ループの単一のラウンドに対して全内部ループを実行します。グリッドと組み合わせを処理する方法です。`break`と`continue`は座っているループだけで機能します。内側のもの。
Junoネストされたループ 内部ループは外側のステップごとに完全に実行されるため、カウントはm倍n倍です。大きな入力を見ます。`break`と`continue`は最も内側のループだけに到達します。両方から脱出するには、フラグを使用するか、内部ループを関数に引き上げてreturnします。
Junoネストされたループ コスト乗算するため、ネストされたループ以上の大きな入力は遅い場所です。時々、dict ルックアップは内側全体置き換えます。すべてのペアリングのために、`itertools.product`はネストを打ちます。`break`は内部ループだけをヒットするため、関数が`return`であるきれいな方法は両方です。

ループ else

Pythonループはbreakをヒットすることなくループが完了した場合にのみ実行されるelse句を持つことができます。それはよく使用されていませんが、「リストを検索し、何も見つからない場合はこれを実行する」という書き方で最もきれいです。

forまたはwhileループのelseは、ループが正常に完了し(イテラブルを排出または条件が偽になる)breakをヒットしないで実行されます。別の検出フラグ変数を必要とせずに、「検索し、見つからない場合報告する」の慣例的なパターンです。

ループのelseは、breakなしでループが終了した場合に実行されます。これはほとんどの人が読むのとは反対です(ループが実行されなかった「else」ではありません)。その命名は正確に読者を混乱させる理由です。そのため、本番のルールはそれを1つのことだけと1つのことだけに使用することです: 検索と見つけないパターン。breakはヒットをマークし、elseはミスを報告します。そうでない場合、それはそれ以外に置き換わります。どこでも他の場所で、知恵はそれが保存するより混乱した査読者より多くのコストです。elseのコメントは行の価値があります。

python
target = "田中"
names = ["太郎", "花子", "一郎"]

for name in names:
    if name == target:
        print(f"Found {target}")
        break
else:
    print(f"{target} not in list")   # break が発生しなかったため実行されます

breakが実行されると、elseはスキップされます。ループがシーケンスを排出すると、elseが実行されます。これはニッチなパターンですが、フラグ変数よりきれいです。

Junoループ else ループは、ループがbreakなしで終了した場合にのみ実行されるelseを持つことができます。それはよく使用されていませんが、「このリストを検索して、何も一致しない場合はこれを実行する」と言う最も良い方法です。名前は少し誤解を招くため、短いコメントが役立ちます。
Junoループ else ループの`else`は通常ループが終了した場合に発生し、`break`なし。それは整然とした「検索と見つからない場合報告する」パターン。見つけたフラグは必要ありません。それをその1つの使用に保ち、その名前が人々を投げ出すため。
Junoループ else `break`なしでループが終了したときに実行されます。これは単語が読む方法の逆です。検索と見つけないケースにそれを保ち、`found = False`フラグを置き換え、コメント。どこでも他の場所で、それはそれが保存するより多くの混乱した査読者のコストです。

ソート

sorted()は新しいソート済みリストを返し、元のリストは変更されません。.sort()はリストをインプレイスでソートし、Noneを返します。key=引数を使用すると、生の値以外でソートできます。例えば、大文字小文字を区別しない名前をソートするか、スコア別のプレイヤータプルをソートしてください。

sorted()は安全なデフォルトです: 元を決して修正しません。.sort()はインプレイスで修正し、Noneを返します。両方とも降順にreverse=Trueを受け入れます。key=引数は比較の前に各要素に適用される関数を取ります。これはソート基準をデータから分離します。

Pythonのソートは安定です。つまり、等しく比較するアイテムは元の順序を保ちます。これにより、ステージでソートできます: 最初に名前でソート、次にスコアでソート。同じスコアを持つプレイヤーは名前の順序のままです。key=関数は比較ごとではなく、要素ごとに1回実行されるため、高コストなキーはn回計算されます。n ログn ではなく時間。そして、あなたはめったに自分でそれをキャッシュする必要があります。.sort()は目的上Noneを返して、scores = scores.sort()を書くのを止め、静かにリストを拭きます。そのため、インプレイス呼び出しは独自の行に立つ必要があります。sorted()をデフォルトに頼り、.sort()を実際に元を相互作用したいときに保ちます。

python
scores = [87, 42, 96, 55, 71]

ranked = sorted(scores)           # [42, 55, 71, 87, 96] (新しいリスト)
scores.sort()                     # 元のリストをソートし、None を返します
scores.sort(reverse=True)         # [96, 87, 71, 55, 42]

両方ともkey=引数を受け入れます: 比較の前に各アイテムに適用される関数:

python
names = ["太郎", "花子", "一郎"]
sorted(names, key=str.lower)       # 大文字小文字を区別しないソート

players = [("太郎", 87), ("花子", 96), ("一郎", 55)]
sorted(players, key=lambda p: p[1])   # スコア別でソート

lambda p: p[1]はワンライン関数です: プレイヤーのタプルを取得してスコアを返します。ラムダはラムダと内包の章で完全に処理されます。

単純なケースの場合、sorted()を使用します。変更したいリストの場合は、.sort()を使用します。

Junoソートsorted() は新しいソート済みリストを配ってオリジナルを単独で残し、.sort() はリスト自体を再配置してNoneを返します。`key=`を渡して、大文字小文字を区別しない名前のような生の値以外でソートします。疑わしい場合、sorted()に頼ります。安全なものです。
Junoソートsorted() はオリジナルに触れず、.sort() はインプレイスで相互作用し、Noneを返します。両方とも`reverse=True`と比較する前に各要素に適用される`key=`関数を取ります。デフォルトは`sorted()`。リストを変更したい場合は.sort()だけを使用します。
Junoソート ソートは安定なため、ステージでソートし、タイは順序を保ちます。`key=`は要素ごとに1回実行され、比較ごと比較ではなく。それを事前にキャッシュしないでください。そして.sort()は目的上Noneを返します: x = x.sort()はリストを拭きます。インプレイス呼び出しを独自の行に保ちます。

実際

スコアをループしてテータルを累積し、合格等級をカウント、サマリーを印刷します:

python
raw_scores = [87, 42, 96, 55, 71, 63]

total = 0
passing = 0

for score in raw_scores:
    total += score
    if score >= 60:
        passing += 1

average = total / len(raw_scores)
print(f"Average: {average:.1f}")
print(f"Passing: {passing}/{len(raw_scores)}")
print(f"Top score: {sorted(raw_scores, reverse=True)[0]}")

ソート順でファイルのリストを処理し、大きすぎるものをスキップ、何ができたかを報告します:

python
files = [
    {"name": "report_jan.csv", "size_mb": 12},
    {"name": "report_feb.csv", "size_mb": 850},
    {"name": "report_mar.csv", "size_mb": 7},
]

MAX_SIZE = 100
skipped = 0

for f in sorted(files, key=lambda x: x["name"]):
    if f["size_mb"] > MAX_SIZE:
        print(f"Skipping {f['name']} ({f['size_mb']} MB, too large)")
        skipped += 1
    else:
        print(f"Processing {f['name']}...")

print(f"\nDone. {skipped} file(s) skipped.")

リクエストログをスキャンしてエラーを検索し、成功した場合、または試行制限に達したときに終了する再試行ループを使用します:

python
requests = [
    {"method": "GET", "path": "/users", "status": 200},
    {"method": "POST", "path": "/users", "status": 201},
    {"method": "GET", "path": "/broken", "status": 500},
]

errors = []

for req in requests:
    if req["status"] >= 400:
        errors.append(req)

if errors:
    print(f"{len(errors)} error(s) in request log:")
    for err in errors:
        print(f"  {err['method']} {err['path']} -> {err['status']}")
else:
    print("All requests succeeded")

attempts = 0
max_retries = 3
success = False

while attempts < max_retries and not success:
    attempts += 1
    print(f"Attempt {attempts}...")
    success = attempts >= 2   # 2番目の試行での成功をシミュレートします

print("Connected" if success else "Failed after all retries")