単項評価 — Short-circuit Evaluation
このトピックを終えると
Python の and/or が内部的にどのように動作するかを理解し、短絡評価を活用した実践的なパターンを使用でき、順序が重要な理由を説明できるようになります。
短絡評価とは
短絡評価 (Short-circuit Evaluation) とは、論理演算において、結果が確定すれば残りを評価しないことです。
# and: 最初の値が False の場合、2 番目の値を評価しないFalse and print("This never runs") # False
# or: 最初の値が True の場合、2 番目の値を評価しないTrue or print("This never runs") # Trueなぜこのように動作するのでしょうか? 論理的に考えれば当然です。
and: 1 つでも False なら全体が False。最初の値が False なら、2 番目の値を評価する必要はありません。or: 1 つでも True なら全体が True。最初の値が True なら、2 番目の値を評価する必要はありません。
and の動作
# and は、最初の falsy な値を返します。または、最後の値を返します。print(0 and 5) # 0 (0 は falsy → ここで停止)print("" and "hello") # "" (空の文字列は falsy)print(None and 42) # None (None は falsy)
print(1 and 5) # 5 (1 は truthy → 次へ → 5 を返す)print("hello" and 42) # 42 (どちらも truthy → 最後の値)print(3 and 2 and 1) # 1 (すべて truthy → 最後の値)print(3 and 0 and 1) # 0 (0 で停止)重要な点: and は、最初の falsy な値を返します。すべて truthy の場合、最後の値を返します。
Python の Falsy な値
# これらはすべて False として評価されますbool(False) # Falsebool(0) # Falsebool(0.0) # Falsebool("") # Falsebool(None) # Falsebool([]) # False (空のリスト)bool({}) # False (空の辞書)
# 残りはすべて Truebool(1) # Truebool("hello") # Truebool([1, 2]) # Trueor の動作
# or は、最初の truthy な値を返します。または、最後の値を返します。print(0 or 5) # 5 (0 は falsy → 次へ → 5 を返す)print("" or "hello") # "hello" (空の文字列は falsy → "hello" を返す)print(None or 42) # 42
print(1 or 5) # 1 (1 は truthy → ここで停止)print("hi" or "bye") # "hi" (最初の値が truthy)print(0 or "" or None) # None (すべて falsy → 最後の値)print(0 or "" or 42) # 42 (最初の truthy な値)重要な点: or は、最初の truthy な値を返します。すべて falsy の場合、最後の値を返します。
実践的なパターン — デフォルト値の設定
# ユーザーが名前を入力しなかった場合、"Guest" を使用します。username = input("Enter name: ") or "Guest"print(f"Hello, {username}")
# 設定値がない場合、デフォルト値を使用します。config = {}db_host = config.get("host") or "localhost"db_port = config.get("port") or 5432or でデフォルト値を設定するパターンです。左側の値が falsy (空の文字列、None など) の場合、右側の値が使用されます。JavaScript でも同様のパターンをよく使用します。
注意: 0 や空の文字列が有効な値である場合は、このパターンは危険です。
# バグ: 0 も falsy なため、デフォルト値が適用されます。count = 0result = count or 10 # 10 (意図: 0 にすべき)
# 修正: 明示的な比較result = count if count is not None else 10 # 0実践的なパターン — 条件付き実行
# and で条件付き実行 (if の代替)data = [1, 2, 3]data and print(f"Data has {len(data)} items") # 出力されます
empty = []empty and print("This won't print") # 出力されません (空のリスト = falsy)# 安全な属性へのアクセスuser = {"name": "Hoon", "address": None}
# address が None の場合、.get() を呼び出さないcity = user.get("address") and user["address"].get("city")print(city) # None (and が None で停止)順序が重要な理由
短絡評価の重要な点は、コストの高い演算を後回しにすることです。
def is_valid_user(user_id): """データベースの検索 — 遅い演算""" print(f"Querying DB for user {user_id}...") return user_id > 0
def has_permission(user_id, action): """権限の確認 — さらに遅い演算""" print(f"Checking permission for {action}...") return True
# Good: 迅速なチェックを最初に行いますuser_id = -1if user_id > 0 and is_valid_user(user_id) and has_permission(user_id, "read"): print("Access granted")# 出力: (何も出力されない — user_id > 0 が False なので、データベースの検索は行われません)
# Bad: 遅いチェックを最初に行いますif is_valid_user(user_id) and user_id > 0: print("Access granted")# 出力: "Querying DB for user -1..." (不要なデータベースの検索が発生)エラーの防止にも役立ちます。
# リストが空の場合、インデックスにアクセスするとエラーが発生しますitems = []
# Bad: IndexError が発生します# if items[0] > 10:
# Good: 空のリストを最初にチェックしますif items and items[0] > 10: print("First item is large")# items が falsy (空のリスト) なので、items[0] は実行されませんデバッグ時の注意点
短絡評価は、デバッグを困難にする可能性があります。
def check_permission(user): print(f"Checking permission for {user['name']}") return user.get("role") == "admin"
def validate_input(data): print(f"Validating: {data}") return len(data) > 0
user = {"name": "Hoon", "role": "admin"}data = "hello"
if check_permission(user) or validate_input(data): print("Granted")
# 出力:# Checking permission for Hoon# Granted# → validate_input は実行されません! (print もされません)or で最初の値が True の場合、2 番目の関数は、呼び出されません。副作用 (ロギング、カウンターの増加) がある関数を条件式に入れると、実行されるかどうかが最初の結果によって異なります。副作用が常に実行されるようにする場合は、条件式の外で最初に呼び出してください。
他の言語との比較
// JavaScript — 同じ動作
const name = userInput || "Guest";
const port = config.port || 3000;
// JavaScript ES2020: ?? (Nullish Coalescing)
const count = 0 ?? 10; // 0 (?? は null/undefined のみをチェックします)
const count2 = 0 || 10; // 10 (|| は falsy 全体をチェックします)# Python には ?? 演算子はありません# 代わりに明示的な比較を使用しますcount = 0result = count if count is not None else 10 # 0JavaScript の ?? (Nullish Coalescing) は、null と undefined のみをチェックするため、0 や空の文字列が有効な値である場合に安全です。Python にはこれに対応する演算子がないため、is not None 比較を明示的に記述する必要があります。
重要なポイントのまとめ
| 演算子 | 短絡条件 | 返り値 |
|---|---|---|
and | 最初の値が falsy | 最初の falsy な値、または最後の値 |
or | 最初の値が truthy | 最初の truthy な値、または最後の値 |
not | (短絡なし) | 常に True または False |
短絡評価を理解することで、次の 3 つが得られます。デフォルト値の設定 (x or default)、安全なアクセス (items and items[0])、パフォーマンスの最適化 (迅速な条件を最初に行う)。ほとんどのプログラミング言語 (JavaScript、Java、C++) も同じルールに従うため、一度理解すればどこでも適用できます。