制御フロー
これまでに書いてきたプログラムはすべて、毎回同じように実行されます。つまり、上から下へ、一行ずつです。単純なスクリプトならそれで十分ですが、実際のプログラムは判断を下したり処理を繰り返したりする必要があります。クイズは答えが正しいかどうかをチェックする必要があります。ゲームはプレイヤーが勝つか負けるまで動き続ける必要があります。この章では、プログラムを分岐させたり繰り返したりする方法を扱います。
比較
判断を下す前に、何かを比較する必要があります。比較演算子はTrueまたはFalseを返します。早めに正しく覚えておくべき最も重要なこと: =は値を代入し、==は2つの値が等しいかどうかをチェックします。これらを混同するのは初心者が最もよくやる間違いの1つです。
5 > 3 # True
5 < 3 # False
5 == 5 # True (注意: 二重等号; =は代入、==は比較)
5 != 3 # True ("等しくない")
5 >= 5 # True ("以上")
5 <= 4 # False ("以下")=と==の違いは、ほぼ全員が早い段階で躓くポイントです。代入(=)は値を格納します。比較(==)は2つの値が同じかどうかをチェックします。
文字列も比較できます。Pythonはアルファベット順に比較します:
"apple" == "apple" # True
"apple" < "banana" # True (aはbより前)
"apple" == "Apple" # False (大文字小文字を区別)条件の組み合わせ
and、or、notは比較を組み合わせます。andは両側がtrueである必要があります。orは少なくとも片側がtrueである必要があります。notは結果を反転します。これらにより、「スコアが合格点 AND ユーザーがアクティブ」のような実世界の条件を表現できます。
age = 25
score = 88
age >= 18 and score >= 80 # True (両方がtrueでなければならない)
age < 18 or score >= 80 # True (少なくとも片方がtrueでなければならない)
not age >= 18 # False (結果を反転する)andは両側を必要とします。orは少なくとも片側を必要とします。notは反転します。
truthyとfalsy
Pythonのすべての値には、たとえTrueやFalseでなくとも、ブール値としての解釈があります。空文字列、ゼロ、空のリスト、Noneはすべて条件内でFalseのように振る舞います。それ以外はすべてTrueのように振る舞います。つまり、if results:はif len(results) > 0:と書かずにリストが空でないかをチェックします。
# これらはすべて条件内でFalseのように振る舞う:
False, 0, 0.0, "", [], {}, (), None
# それ以外はすべてTrueのように振る舞うこれは、if results:が「リストが空でない場合」と言う自然な方法であり、if name:は文字列に何かしらの内容があるかをチェックすることを意味します。
if / elif / else
if文は条件がTrueの場合のみコードブロックを実行します。elifは最初の条件がfalseだった場合にチェックする条件を追加します。elseはどの条件にも一致しなかったすべてを捕捉します。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は構造を定義するためにそれを使います。一貫性のないインデントは構文エラーになります。
一行の条件
シンプルなyes/no代入のために、Pythonには三項式と呼ばれるコンパクトな一行形式があります: value_if_true if condition else value_if_false。ロジックが本当にシンプルで文章のように読める場合のみ使ってください。
label = "pass" if score >= 50 else "fail"これは三項式です。文章のように読めます。ロジックが本当にシンプルな場合に使ってください。elifを含むものには、完全版を書いてください。
whileループ
whileループは条件がTrueである限りそのブロックを繰り返します。ループが何回実行されるべきか事前にわからない場合、たとえば有効な入力を待つときやジョブが成功するまで再試行するときに使ってください。
lives = 3
while lives > 0:
print(f"Lives remaining: {lives}")
lives -= 1
print("Game over")whileは、ループが何回実行されるか事前にわからない場合に最適です。わかっている場合、またはコレクションを反復処理する場合は、forの方が明確です。
breakとcontinue
breakは反復が何回残っていてもループを即座に終了します。continueは現在の反復の残りをスキップし、条件チェックに戻ります。両方とも、それが内側にある最も内側のループにのみ影響します。
breakはループを即座に終了します:
target = 5
num = 0
while True:
num += 1
if num == target:
print(f"Found {target}")
break # ループを停止breakを含むwhile True:は、終了条件が複雑な場合やループ本体の最後で発生する必要がある場合の、有効でよく使われるパターンです。
continueは現在の反復の残りをスキップし、条件チェックに戻ります:
num = 0
while num < 10:
num += 1
if num % 2 == 0:
continue # 偶数をスキップ
print(num) # 奇数のみ表示: 1, 3, 5, 7, 9forループ
forループはシーケンスを1つずつ処理します: リスト、文字列、数値の範囲などです。forの後に指定した変数は、順番に各項目を受け取ります。自分でカウンタを管理したり長さをチェックしたりする必要はありません。
players = ["はると", "ゆい", "そうた"]
for player in players:
print(f"Hello, {player}!")forループは文字列(文字ごとに反復)や他の任意のシーケンス型でも機能します。
range()
range()はループするための数値のシーケンスを生成します。range(5)は0, 1, 2, 3, 4を返します。開始、終了、ステップサイズを制御できます。特定の回数ループを実行する必要がある場合に使ってください。
for i in range(5):
print(i) # 0, 1, 2, 3, 4range()には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つずつ数値を生成するので、非常に大きな範囲でも効率的です。
enumerate()
enumerate()はループしながらインデックスと値の両方を提供するので、カウンタを別途追跡する必要はありません。i, playerの部分は、各反復で自動的にペアの値を受け取ります。
players = ["はると", "ゆい", "そうた"]
for i, player in enumerate(players):
print(f"{i + 1}. {player}")
# 1. はると
# 2. ゆい
# 3. そうたi, player構文はアンパックと呼ばれます。Pythonは(index, value)ペアを自動的に2つの名前に分割します。
デフォルトではenumerate()は0から始まります。それを変更するには開始値を渡してください:
for i, player in enumerate(players, start=1):
print(f"{i}. {player}") # 1から始まるネストしたループ
ループの中に別のループを置くことができます。内側のループは、外側のループの1回の反復ごとに完全に実行されます。これは、グリッド、組み合わせ、または2レベルの構造を持つあらゆるデータを処理する方法です。
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ネストしたループ内のbreakとcontinueは最も内側のループにのみ影響します。
Loop-else
Pythonのループには、breakに遭遇せずにループが終了した場合のみ実行されるelse句を付けることができます。あまり一般的に使われませんが、「リストを検索して、何も見つからなかった場合にこれを行う」と書く最もクリーンな方法です。
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が実行されます。これはニッチなパターンですが、フラグ変数よりも明確です。
ソート
sorted()は新しいソート済みリストを返し、元のリストは変更しません。.sort()はリストをその場でソートし、Noneを返します。key=引数を使うと、生の値以外のもので並べ替えることができます。たとえば、名前を大文字小文字を区別せずにソートしたり、プレイヤーのタプルをスコアでソートしたりできます。
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=引数を受け入れます: 比較前に各項目に適用される関数です:
names = ["そうた", "はると", "ゆい"]
sorted(names, key=str.lower) # 大文字小文字を区別しないソート
players = [("はると", 87), ("ゆい", 96), ("そうた", 55)]
sorted(players, key=lambda p: p[1]) # スコアでソートlambdaとは?
lambda p: p[1]は一行関数です。プレイヤーのタプルを受け取り、スコアを返します。lambda関数はLambda、内包表記、zipの章で扱います。
シンプルなケースには、sorted()を使ってください。その場で変更したいリストには、.sort()を使ってください。
実践
スコアをループ処理し、合計を累積し、合格点をカウントし、サマリーを出力します:
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]}")
