まえがき
今回は制御構文、具体的には、if, match, for, while, 例外処理について扱う。ただし、どの本にも書いてあるような構造については、深く扱うようなことはしない。その代わり、match 文を使うべき場面など、あまり書籍には取り上げられない項目については、掘り下げ気味に扱う。
if 文
| 構造 | 構文 | 特徴 | 例 |
|---|---|---|---|
| 単純分岐 | if 条件: |
条件が True のときだけ処理 | if x>0: |
| 二分岐 | if ... else |
True なら処理1、False なら処理2 | 正負判定 |
| 多岐分岐 | if ... elif ... else |
条件を順に判定、最初の True の処理だけ実行 | 評価判定 |
| ネスト |
if の中に if
|
条件を段階的に分岐 | 複合条件 |
単純分岐( if )
if 条件式:
処理
条件式が True の場合のみ処理が実行されるが、False の場合は何も実行されない。
x = 10
if x > 0:
print("xは正の数です")
# 出力: xは正の数です
単純分岐( if-else )
if 条件式:
処理1
else:
処理2
条件式が True の場合は 処理1 が実行されるが、False の場合は 処理2 が実行される。
x = -5
if x > 0:
print("xは正の数です")
else:
print("xは正の数ではありません")
# 出力: xは正の数ではありません
多岐分岐( if-elif-else )
if 条件式:
処理1
else:
処理2
上から順に評価され、最初に True になった条件の処理が実行される。どの条件も満たさなければ else が実行される。
score = 75
if score >= 90:
print("評価: A")
elif score >= 70:
print("評価: B")
elif score >= 50:
print("評価: C")
else:
print("評価: D")
# 出力: 評価: B
ネスト構造の分岐(入れ子の if )
if 条件1:
if 条件2:
処理1
else:
処理2
else:
処理3
if 文の中にさらに if 文を入れることで、条件を細分化できる。条件が複雑になる場合は可読性が下がるため、論理演算子(and, or, not)を使う方が推奨されることも多い。
x = 10
y = -5
if x > 0:
if y > 0:
print("xもyも正の数です")
else:
print("xは正の数ですがyは正の数ではありません")
else:
print("xは正の数ではありません")
# 出力: xは正の数ですがyは正の数ではありません
条件式を指定する際の注意点
| 注意点 | 説明 | 推奨方法 |
|---|---|---|
| 真理値評価 | 空の値は False、それ以外は True |
if not list: など |
== vs is
|
等価性と同一性の違い | 値比較は ==、None判定は is None
|
| 比較演算子チェーン |
0 < x < 10 と書ける |
可読性に注意 |
| 短絡評価 | 左が確定すれば右を評価しない | 副作用ある式を注意して書く |
| None 判定 |
== None は避ける |
is None |
| 包含判定 | コレクションに要素が含まれるか | "x in list" |
| 浮動小数点比較 | 誤差があるため直接比較不可 | math.isclose() |
match ... case 文
基本概要
match 文:Pythonにおける構造的パターンマッチング機能
→ 他言語の swich-case と同様といわれることも多いが、値の比較にとどまらず、構造分解や型判定も可能
match 式:
case パターン1:
処理1
case パターン2:
処理2
case _:
デフォルト処理
使用例は以下。
command = "start"
match command:
case "start":
print("処理を開始します")
case "stop":
print("処理を停止します")
case _:
print("未知のコマンドです")
# 処理を開始します
case _:はワイルドカードを示す。(どの条件も当てはまらなかった場合)
match 文を使うべき場面
[3] には以下のような節がある。
Item 9: Consider match for Destructuring in Flow Control; Avoid When if Statements Are Sufficient.
すなわち、「構造的パターンマッチングの場合には、match 文の使用を考えて、そもそも if 文の使用で事足りるなら、 match 文は避けるべき。」という記述である。
個人的に感じる、match 文の使いどころは以下の3種類
・ 条件が複雑/多岐にわたる場合
・ 構造的パターンマッチング
・ 型ごとの処理
逆にこれらのいずれも満たさない場合は、if 文で充分であるし、match 文を用いる必要はないと感じる。
今回は技術ブログであるし、経度緯度の例をサラッと扱って終わるようなテキストがほとんどのため、一応ある程度網羅的に扱う。
1. リテラルパターン
→ 数値や文字列、None などのリテラル値と一致するとマッチする
def literal_demo(x):
match x:
case 0:
return "zero"
case "yes":
return "affirmative"
case None:
return "is None"
case _:
return "other"
print(literal_demo(0))
print(literal_demo("yes"))
print(literal_demo(None))
print(literal_demo(42))
zero
affirmative
is None
other
2. ワイルドカードパターン
→ _ はどんな値にもマッチし、変数としては束縛しない
def wildcard_demo(x):
match x:
case _:
return "always matches"
print(wildcard_demo("anything"))
always matches
3. キャプチャパターン
→ 値を新しい変数に束縛する。常にマッチする。
def capture_demo(x):
match x:
case y:
return f"captured: {y}"
print(capture_demo([1, 2, 3]))
captured: [1, 2, 3]
4. 値パターン
→ ドット参照を用いると、変数でなく既存の定数や属性と比較できる。
import math
def value_demo(x):
match x:
case math.pi:
return "exactly math.pi"
case math.e:
return "exactly math.e"
case _:
return "other"
print(value_demo(math.pi))
print(value_demo(math.e))
print(value_demo(3.14159))
exactly math.pi
exactly math.e
other
5. グループパターン
→ () でパターンをまとめて、優先順位を明示する
def group_demo(x):
match x:
case ("ok" | "yes") | ("y" | "1"):
return "affirmative"
case _:
return "other"
print(group_demo("y"))
print(group_demo("no"))
affirmative
other
6. シーケンスパターン
→ リストやタプルなどのシーケンスを分解できる。*rest で残りを受け取ることが出来る。
def sequence_demo(seq):
match seq:
case [x, y]:
return f"pair: {x}, {y}"
case [head, *middle, tail]:
return f"head={head}, middle={middle}, tail={tail}"
case []:
return "empty"
case _:
return "other"
print(sequence_demo([10, 20]))
print(sequence_demo((1, 2, 3, 4)))
print(sequence_demo([]))
7. マッピングパターン
→ 辞書のキーと値をパターンとして指定可能。**rest で残りを受け取ることが出来る。
def mapping_demo(d):
match d:
case {"type": "user", "id": uid, "name": name, **rest}:
return f"user id={uid}, name={name}, rest={rest}"
case {"type": "system"}:
return "system object"
case _:
return "unknown"
print(mapping_demo({"type": "user", "id": 7, "name": "Taro", "role": "admin"}))
print(mapping_demo({"type": "system", "uptime": 123}))
user id=7, name=Taro, rest={'role': 'admin'}
system object
8. クラスポジショナル/キーワードパターン
→ データクラスやクラスの属性を分解できる。
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def class_demo(obj):
match obj:
case Point(0, 0):
return "origin"
case Point(x, 0):
return f"on X axis at {x}"
case Point(x=0, y=y):
return f"on Y axis at {y}"
case Point(x, y):
return f"point({x}, {y})"
case _:
return "not a Point"
print(class_demo(Point(0, 0)))
print(class_demo(Point(5, 0)))
print(class_demo(Point(0, -2)))
print(class_demo(Point(3, 4)))
origin
on X axis at 5
on Y axis at -2
point(3, 4)
9. or パターン
| で複数のパターンのいずれかにマッチする。
def or_demo(x):
match x:
case 0 | 0.0 | False:
return "falsy zero"
case "y" | "yes" | "ok":
return "affirmative"
case _:
return "other"
print(or_demo(0.0))
print(or_demo("ok"))
print(or_demo(2))
falsy zero
affirmative
other
10. as パターン
→ 部分パターンにマッチしつつ、全体を変数に束縛する
def as_demo(obj):
match obj:
case {"type": "user", "id": uid} as whole:
return f"user #{uid}, whole={whole}"
case Point(x, y) as p:
return f"Point captured: {p} (x={x}, y={y})"
case _:
return "no match"
print(as_demo({"type": "user", "id": 99, "name": "Ai"}))
print(as_demo(Point(1, 2)))
user #99, whole={'type': 'user', 'id': 99, 'name': 'Ai'}
Point captured: Point(x=1, y=2) (x=1, y=2)
上記の1~10のうち、どのケースで match 文を用いるべきかを明記する。基本的に、7のマッピングパターンを除き(他には1、2、6、8も許容可能に感じる)、代替可能なので、書けるのであれば if 文で書くべきである。しかし、条件分岐が多量多岐に渡る場合は、match 文でも代用に値する。
while 文
基本概念
while 条件式:
実行する処理
else:
条件式が偽になって正常終了したときに実行する処理(任意)
条件式が True の間、ブロック内の処理を繰り返し、条件式が False となるとループを終了。else 節は break で抜けなかった場合に実行される。
count = 0
while count < 3:
print("count:", count)
count += 1
else:
print("正常終了")
count: 0
count: 1
count: 2
正常終了
while 文と制御構文
break による中断
→ ループを強制終了する。else 節はスキップされる。
n = 0
while n < 5:
if n == 3:
break
print(n)
n += 1
else:
print("正常終了")
0
1
2
continue によるスキップ
→ 残った処理をスキップして、次のループに移る。
n = 0
while n < 5:
n += 1
if n % 2 == 0:
continue
print(n)
1
3
5
ネストされた while 文
→ 入れ子構造も可能。この場合、外側/内側でカウンタを管理する必要がある。
i = 1
while i <= 3:
j = 1
while j <= 2:
print(f"i={i}, j={j}")
j += 1
i += 1
i=1, j=1
i=1, j=2
i=2, j=1
i=2, j=2
i=3, j=1
i=3, j=2
これを応用して、以下のように素数を検索するコードも書ける。
n = 2
while n < 20:
is_prime = True
i = 2
while i < n:
if n % i == 0:
is_prime = False
break
i += 1
if is_prime:
print(n, "は素数")
n += 1
2 は素数
3 は素数
5 は素数
7 は素数
11 は素数
13 は素数
17 は素数
19 は素数
while True: による条件の後判定
基本的な構文は以下。
while True:
# ループ処理
print("必ず1回は実行される")
# 条件判定(Falseならループを抜ける)
if not 条件式:
break
具体例は以下。
n = 1
while True:
print(n)
n *= 2
if n > 100:
break
1
2
4
8
16
32
64
128
for 文
for 文の基本構造
基本構造は以下。
for 変数 in iterable:
実行する処理
else:
breakされずに終了したときに実行する処理(任意)
基本例は以下。
for i in range(3):
print("i:", i)
else:
print("正常終了")
i: 0
i: 1
i: 2
正常終了
for 文の便利な組み合わせ
enumerate
→ インデックスと要素を同時に取り出す
words = ["a", "b", "c"]
for idx, w in enumerate(words, start=1):
print(idx, w)
1 a
2 b
3 c
zip
→ 複数のイテラブルを並列に処理
names = ["A", "B", "C"]
scores = [90, 80, 70]
for n, s in zip(names, scores):
print(n, s)
A 90
B 80
C 70
dict のループ
d = {"x": 1, "y": 2}
for key, value in d.items():
print(key, value)
x 1
y 2
ネストされた for 文
for i in range(1, 10):
for j in range(1, 10):
print(f"{i*j:2}", end=" ")
print()
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
...
フラグ変数によるループの脱出
for/while-else の代わりに、フラグ変数を用いて脱出することも可能。
nums = [1, 3, 5, 7, 9]
flag = False
for n in nums:
if n % 2 == 0: # 偶数を発見したら
flag = True
break
if flag:
print("偶数が見つかりました")
else:
print("偶数は存在しません")
偶数は存在しません
例外処理
今回は例外処理について扱う。
例えばアプリ開発において、様々なエラーに直面するが、このうち事前対処が可能か不可能かによって大別できる。
→ 対処可能:(いわゆる)構文エラー、対処不可能:例外
後者の例外を、例外処理にて対処することになる。
基本的な構文を先にあげておく
try:
# 例外が発生する可能性のある処理
except エラー型 as 変数:
# 例外が発生した場合の処理
else:
# 例外が発生しなかった場合の処理
finally:
# 例外の有無にかかわらず必ず実行される処理
例外を処理するー try 文
例外処理:あらかじめ発生するかもしれないエラーを想定しておき、実行を継続するため/安全に終了ための処理
最もシンプルなのは、try-except 命令
try:
# 例外が発生する可能性のある処理
except エラーの種類:
# 例外が発生したときの処理
try ブロックで例外が起きると、そこで処理が中断し except ブロックに移行。ただし、except に指定しなかった例外は処理できずそのまま送出される。
具体例は以下。以下のように、except を複数書くことで、例外ごとに異なる処理が可能。
try:
value = int("abc")
except ValueError:
print("数値に変換できません")
except TypeError:
print("型が不正です")
else 節は、例外が発生しなかった場合に実行される。正常系とエラー形を分離できるので、可読性が向上する。
try:
value = int("123")
except ValueError:
print("変換失敗")
else:
print("変換成功:", value)
finally は、例外の有無にかかわらず実行れさ、ファイルやネットワーク接続などのリソース解法に用いられる。
try:
f = open("sample.txt", "r")
data = f.read()
except FileNotFoundError:
print("ファイルが存在しません")
finally:
print("必ず実行される処理")
if 'f' in locals() and not f.closed:
f.close()
raise で明示的に例外を送出できる。条件違反や入力不正の建設に利用できる。
def divide(a, b):
if b == 0:
raise ZeroDivisionError("0で割ることはできません")
return a / b
raise とだけ書くと、補足した例外を再度送出する。ログを記録してから上位に伝えるなどの使い方ができる。
Exception を継承したクラスを作成し、アプリケーション固有のエラー処理を実現できる。
class CustomError(Exception):
pass
try:
raise CustomError("独自エラーです")
except CustomError as e:
print("捕捉:", e)
まとめ
| 節 | 実行される条件 |
|---|---|
try |
常に実行される |
except |
指定例外が発生したとき |
else |
例外が発生しなかったとき |
finally |
常に実行される(終了処理に必須) |
参考文献
[1] 独習Python 第2版 (2025, 山田祥寛, 翔泳社)
[2] Pythonクイックリファレンス 第4版(2024, Alex, O’Reilly Japan)
[3] Effective Python: 125 Specific Ways to Write Better Python, 3rd Edition (2025, Brett Slatkin, O’Reilly)