Python | データ構造強化:内包表記で条件付き

Python Python
スポンサーリンク

概要(内包表記の「条件付き」は“選ぶ・変換する”を1行で完結させる技)

内包表記に条件を組み込むと、要素を選び(フィルタ)ながら、必要に応じて変換(if-else)までを1行で書けます。for+appendの定番より短く、何をしたいかが明確になります。重要なのは「フィルタ if は末尾」「if-else は式の中」「条件の順番を軽い→重いにする」の3点です。これを守るだけで、読みやすくて速いコードになります。

基本の使い方(ここが重要)

フィルタ条件(if)は最後に書く

内包表記の構文は「[式 for 変数 in イテラブル if 条件]」。末尾の if は“取り込むか捨てるか”のフィルタです。

# 0〜9から偶数だけ選ぶ
evens = [x for x in range(10) if x % 2 == 0]
Python

「条件は軽く」「変換は後で」を基本線にすると、無駄な計算を減らせます。

変換を条件で分岐する(if-else は式の中に書く)

「Aならこう変換、Bなら別の変換」を1行にできます。ここでの if-else は“式”で、フィルタではありません。

# 偶数はそのまま、奇数は二乗にする
mixed = [x if x % 2 == 0 else x * x for x in range(6)]
Python

「フィルタの if(末尾)」と「式の if-else(先頭)」は役割が違う、が最重要ポイントです。

重要ポイントの深掘り(順序最適化・複数条件・例外と欠損の扱い)

条件の順序は「軽い→重い」にする

最初に安い判定で間引き、重い計算は後回しにします。計算コストの高い関数や正規表現は、必要な要素にだけ適用するのが鉄則です。

def heavy(x): return x**3 - x**2 + x   # 例:重い
def cheap(x): return x % 2 == 0        # 軽い

result = [heavy(x) for x in range(10_000) if cheap(x)]
Python

複数条件は「and / or」で組み合わせる

複合条件は短絡評価(先に false なら後は評価しない)を活かせます。これも軽い→重いの順に並べます。

# 100未満かつ3の倍数だけ
vals = [x for x in data if x < 100 and x % 3 == 0]
Python

None や欠損を安全に扱う(guard を先に)

比較できないものが混じると例外の原因になります。まず“存在ガード”で落とし、次に比較します。

rows = [{"score": 85}, {"score": None}, {"score": 92}]
safe = [r for r in rows if r["score"] is not None and r["score"] >= 90]
Python

例外発生を避ける(内包に try/except は入れない)

内包は“作る”ための構文です。失敗しやすい処理は小さな関数に切り出して使うのが正解です。

def safe_int(s):
    try:
        return int(s)
    except ValueError:
        return None

nums = [n for n in (safe_int(s) for s in strings) if n is not None]
Python

実務での使いどころ(テキスト整形・抽出・辞書/集合内包の条件)

テキストの抽出と整形(軽いフィルタ→正規表現)

import re
pat = re.compile(r"[A-Za-z]{3,}")
# 空・短い文字列を捨ててから正規表現
tokens = [m.group(0).lower() 
          for s in lines if s and len(s) >= 3
          for m in pat.finditer(s)]
Python

辞書内包の条件(キーや値で選んで変換)

rows = [{"name":"alice","score":85},{"name":"bob","score":70}]
passed = {r["name"]: r["score"] for r in rows if r["score"] >= 80}
Python

辞書内包は「何をキーに、何を値に」するかが明確で、読みやすさが高いです。

集合内包で重複排除しつつ条件抽出

# 拡張子集合(空拡張子を捨てる)
exts = {p.suffix.lower() for p in paths if p.suffix}
Python

集合内包は自動的に重複が消えるので、正規化に向きます。

ネスト内包で二次元データを条件付きで平坦化

grid = [[1,2,3],[4,5,6],[7,8,9]]
# 偶数だけをフラットに
evens = [v for row in grid for v in row if v % 2 == 0]
Python

よくある落とし穴の回避(副作用禁止・過度な複雑化・可読性)

副作用(print・書き込み)を内包に入れない

内包は“作る”場所。副作用は通常の for で分けると、意図が明確になり安全です。

values = [f(x) for x in data if cond(x)]  # 生成
for x in values: print(x)                 # 出力
Python

過度なネストや条件は分割する

読みにくくなったら素直に分けます。途中結果に名前を与えるだけで理解度が跳ね上がります。

filtered = (x for x in items if cheap_check(x))
result   = [heavy(x) for x in filtered if heavy_check(x)]
Python

速度が欲しいなら「前取り」する

ループ内で何度も使う関数や属性参照は外で束縛し、コストを下げます。

lower = str.lower
normalized = [lower(w) for w in words if w]
Python

例題で身につける(定番から一歩先まで)

例題1:フィルタ+変換(順序最適化)

users = [{"name":"alice","age":25},
         {"name":"bob","age":0},
         {"name":"cara","age":30}]
adults = [u["name"].capitalize() for u in users if u["age"] >= 20]
Python

例題2:if-else でラベル付け

labels = ["偶数" if x % 2 == 0 else "奇数" for x in range(1, 6)]
Python

例題3:None を最後に送る安全キー(sorted と併用)

rows = [{"name":"alice","score":85},
        {"name":"bob","score":None},
        {"name":"cara","score":92}]
safe_key = lambda r: (r["score"] is None, r["score"] if r["score"] is not None else float("inf"))
sorted_rows = sorted(rows, key=safe_key)
passed = [r for r in sorted_rows if r["score"] is not None and r["score"] >= 80]
Python

例題4:辞書/集合内包の条件付き

items = {"A":3, "b":2, "C":3, "d":1}
# 値が2以上だけ、小文字キーへ変換
filtered = {k.lower(): v for k, v in items.items() if v >= 2}

names = ["Alice","","Bob","Cara","Bob"]
unique = {n.lower() for n in names if n}   # 空を除外して重複排除
Python

まとめ

「条件付きの内包表記」は、フィルタ(末尾の if)と変換(式の if-else)を正しく使い分けるのが核心です。条件の順序は軽い→重いにし、None など比較不可の値は先にガードして安全に。例外を伴う処理は関数へ切り出し、副作用は内包から分離する。辞書・集合・ネスト内包まで広げれば、抽出・整形・正規化を短く、速く、堅牢に書けます。まずは小さなデータで「フィルタ→変換」の型を体に入れ、読みやすさを最優先に設計しましょう。

タイトルとURLをコピーしました