and / or の短絡評価を活用したイディオム集(Python)

Python の andor は単なる論理演算子ではない。短絡評価(short-circuit evaluation)という仕組みにより、条件が確定した時点で評価を打ち切り、最後に評価した値をそのまま返す。この特性を理解すると、簡潔で Python らしいコードが書けるようになる。

短絡評価の基本ルール

andor の評価ルールは以下の通りだ。

and の評価

左辺が Falsy なら左辺を返す。左辺が Truthy なら右辺を返す。

or の評価

左辺が Truthy なら左辺を返す。左辺が Falsy なら右辺を返す。

重要なのは、返り値が TrueFalse ではなく「最後に評価したオブジェクトそのもの」という点だ。

# and は左辺が Falsy なら左辺を返す
print(0 and "hello")      # 0
print("" and [1, 2, 3])   # ""

# and は左辺が Truthy なら右辺を返す
print(1 and "hello")      # "hello"
print([1] and {"a": 1})   # {"a": 1}

# or は左辺が Truthy なら左辺を返す
print(1 or "hello")       # 1
print("hi" or [])         # "hi"

# or は左辺が Falsy なら右辺を返す
print(0 or "hello")       # "hello"
print("" or "default")    # "default"

イディオム 1:デフォルト値の設定

or を使った最も一般的なイディオムが、デフォルト値の設定だ。

# ユーザー入力が空なら "Guest" を使う
name = user_input or "Guest"

# 辞書から取得した値が None や空なら代替値を使う
config = settings.get("timeout") or 30

# 環境変数が未設定ならデフォルトを使う
import os
debug_mode = os.environ.get("DEBUG") or "false"

ただし、このイディオムには落とし穴がある。0False も Falsy なので、これらが有効な値として使われる場合は意図しない動作になる。

# 落とし穴:0 が有効な値の場合
count = user_count or 10  # user_count が 0 だと 10 になってしまう

# 対策:None との比較を明示する
count = user_count if user_count is not None else 10

# Python 3.8 以降ならウォルラス演算子も使える
count = 10 if (c := user_count) is None else c

イディオム 2:条件付き関数呼び出し

and を使うと、条件が成立したときだけ関数を呼び出せる。

# callback が設定されていれば呼び出す
callback and callback()

# debug フラグが True なら出力する
debug and print(f"[DEBUG] value = {value}")

# リストが空でなければ最初の要素を処理する
items and process(items[0])

これは以下の if 文と等価だが、1 行で書けるため式として扱いたい場合に便利だ。

# 上記と等価な if 文
if callback:
    callback()

if debug:
    print(f"[DEBUG] value = {value}")

if items:
    process(items[0])

イディオム 3:最初の Truthy 値を取得

複数の候補から最初に見つかった Truthy 値を取得するパターン。

# 複数の設定源から最初に見つかった値を使う
import os

config_value = (
    os.environ.get("APP_CONFIG")
    or load_from_file()
    or load_from_database()
    or "default"
)

# ユーザー名の優先順位付き取得
display_name = user.nickname or user.username or user.email or "Anonymous"

このパターンでは、左から順に評価され、最初に Truthy な値が見つかった時点で残りは評価されない。つまり load_from_file() が Truthy を返せば load_from_database() は呼ばれない。

イディオム 4:すべての条件を満たす最後の値

and を連鎖させると、すべてが Truthy な場合のみ最後の値を返せる。

# すべての条件を満たす場合のみ結果を返す
result = user and user.is_active and user.permissions and "allowed"

# 上記は以下と等価
if user and user.is_active and user.permissions:
    result = "allowed"
else:
    result = None  # または Falsy だった値

イディオム 5:辞書やリストの安全なアクセス

短絡評価を使って、存在確認とアクセスを同時に行える。

# 辞書のネストした値を安全に取得
data = {"user": {"profile": {"name": "Alice"}}}

# and でチェーンすると途中で None なら止まる
name = data.get("user") and data["user"].get("profile") and data["user"]["profile"].get("name")

# ただし、これは複雑になりがち
# Python 3.8 以降ならウォルラス演算子で改善できる
if (user := data.get("user")) and (profile := user.get("profile")):
    name = profile.get("name")

より現代的なアプローチとしては、dict.get() のチェーンや例外処理、あるいは専用ライブラリの使用が推奨される。

イディオム 6:三項演算子の代替

古い Python コードでは、orand を組み合わせて三項演算子の代わりにしていた。

# 古いイディオム(Python 2.4 以前)
result = condition and true_value or false_value

# 現在の三項演算子(Python 2.5 以降)
result = true_value if condition else false_value

ただし、古いイディオムには true_value が Falsy の場合に正しく動作しないバグがある。現在は三項演算子を使うべきだ。

# バグの例
condition = True
true_value = 0  # Falsy
false_value = 100

# 古いイディオム:期待は 0 だが 100 が返る
result = condition and true_value or false_value
print(result)  # 100(バグ!)

# 三項演算子:正しく 0 が返る
result = true_value if condition else false_value
print(result)  # 0

短絡評価の副作用を利用するパターン

短絡評価では、評価されなかった式の副作用は発生しない。これを意図的に利用することもある。

# ログ出力を条件付きで行う
verbose and log.debug(f"Processing {item}")

# キャッシュがなければ計算する
cached_value or compute_and_cache()

# ファイルが存在しなければ作成する
os.path.exists(path) or create_file(path)

ただし、副作用を伴う式を短絡評価に含めると、コードの意図が分かりにくくなる。可読性を優先するなら、明示的な if 文を使う方がよい場合も多い。

まとめ

短絡評価のイディオムは Python らしい簡潔なコードを書くのに役立つが、使いすぎると可読性を損なう。以下の指針を参考にするとよい。

推奨されるケース

デフォルト値の設定(value or default)、単純な条件付き実行(flag and action())など、意図が明確なパターン。

避けるべきケース

複雑なネスト、副作用を伴う複数の式、0False が有効値になりうる場面。明示的な if 文や三項演算子を使う方がよい。