Pythonのin演算子は、リストに特定の要素が含まれるかをシンプルに判定できる強力な道具です。
for文で総当たりしなくても、1行で可読性と安全性を両立できます。
本記事では、基本構文からつまずきやすいポイント、ネストや辞書の応用、さらに高速化のコツまで段階的に丁寧に解説します。
in演算子でリストに値が含まれるか判定
構文(value in list)
基本構文
リストに要素が含まれるかどうかはvalue in some_listで判定します。
結果はTrueまたはFalseのブール値です。
# 基本構文の例
fruits = ["apple", "banana", "orange"]
# "banana" がリストにあるか確認
print("banana" in fruits) # True
# "grape" がリストにあるか確認
print("grape" in fruits) # False
True
False
演算子inは「左辺の値が右辺のコンテナに含まれているか」を調べます。
右辺はリストのほか、文字列、タプル、セット、辞書(キーを対象)などにも使えます。
戻り値(bool)と判定結果
ブール値として返る
戻り値はbool型で、if文や三項演算子などでそのまま使用できます。
numbers = [2, 4, 6, 8]
exists = 4 in numbers # bool の True が入る
print(type(exists), exists)
# if での分岐
if 10 in numbers:
print("10は含まれます")
else:
print("10は含まれません")
<class 'bool'> True
10は含まれません
典型パターン
存在チェックは副作用なく読みやすい条件式になります。
早期リターンなどと組み合わせるとコードが明快になります。
否定形(not in)の使い方
含まれていないことを確認する
含まれていないことを確かめたい場合はnot inを使います。
blocked_users = ["alice", "bob"]
user = "carol"
if user not in blocked_users:
print(f"{user} はアクセス可能です")
else:
print(f"{user} はブロックされています")
carol はアクセス可能です
読みやすさのコツ
肯定形と否定形は読み手に与える印象が異なります。
ポリシーに合わせて「許可リスト」または「拒否リスト」で管理すると条件が分かりやすくなります。
例(数値リスト/文字列リスト)
数値リストの例
scores = [55, 70, 85, 90]
print(70 in scores) # True
print(100 in scores) # False
True
False
文字列リストの例
langs = ["Python", "JavaScript", "Go"]
print("Python" in langs) # True
print("python" in langs) # False (大文字小文字は区別される)
True
False
初心者がつまずくポイント
部分一致ではない(文字列の誤解に注意)
リスト要素は「完全一致」で比較される
リストに対するinは部分一致ではありません。
各要素と左辺の値を==で比較し、一致する要素があるかを調べます。
words = ["banana"]
print("na" in words) # False: リスト要素とは部分一致しない
print("banana" in words) # True: 完全一致はOK
# 文字列そのものに対する in は「部分文字列」検索になる点に注意
print("na" in "banana") # True: こちらは文字列の部分一致
False
True
True
よくある誤解
「リストの中の文字列に部分一致するか」を調べるには、要素ごとにチェックする必要があります。
words = ["apple", "banana", "orange"]
needle = "na"
print(any(needle in w for w in words)) # True ("banana" に "na" が含まれる)
True
型の違い(1と’1’は別物)
文字列と数値は別型
1と'1'は別物です。
型が異なると==は通常Falseになります。
values = [1, 2, 3]
print("1" in values) # False: 文字列 "1" は含まれない
print(1 in values) # True
False
True
メモ
ブール値は数値と等価比較されることがあるため(例: True == 1 はTrue)、混在リストでは思わぬ一致が起きます。
型をそろえるか、データ設計でブールと数値を混在させないのが安全です。
大文字小文字の違い(case sensitive)
大文字小文字は区別される
names = ["Alice", "Bob"]
print("alice" in names) # False
print("Alice".lower() in [n.lower() for n in names]) # True: 正規化して比較
False
True
正規化の実践
比較前に小文字化(または大文字化)しておくことで入力ゆらぎを吸収できます。
空リストとNoneの判定
空リストに対する in
空リスト[]に対するinは常にFalseです。
empty = []
print(1 in empty) # False
False
Noneと空リストは別物
Noneと空リストは異なるため、判定は目的に応じて分けます。
data = None
# None かどうかは is で判定
if data is None:
print("データがありません(None)")
# 空リストかどうかは長さや真偽値で判定
data = []
if not data: # len(data) == 0 と同義
print("空リストです")
データがありません(None)
空リストです
- 関連記事:Noneとは?意味と使い方をやさしく解説
ネストや複合データでの存在確認
ネストしたリストの探索(anyの併用)
any を使った内側探索
2次元リストに値が含まれるかは、各サブリストに対してinを適用しanyでまとめます。
grid = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
]
target = 4
found = any(target in row for row in grid)
print(found) # True
True
位置も知りたい場合
# 見つかった最初の位置(行, 列)を返す
def find_position(grid, value):
for r, row in enumerate(grid):
if value in row:
c = row.index(value) # ここは発見済みなので安全に index を使える
return r, c
return None
print(find_position(grid, 7)) # (2, 1)
(2, 1)
リスト内の辞書でキー/値を探すコツ
値で探す
users = [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
{"id": 3, "name": "Carol"},
]
# id が 2 のユーザーが存在するか
exists = any(u.get("id") == 2 for u in users)
print(exists) # True
# name が "Bob" の辞書だけ抽出
matched = [u for u in users if u.get("name") == "Bob"]
print(matched)
True
[{'id': 2, 'name': 'Bob'}]
キーの存在で探す
辞書自体へのinはキーの存在を調べます。
リスト内の各辞書に同じキーがあるかを検査する例です。
# すべての要素が "name" キーを持つか
all_have_name = all("name" in u for u in users)
print(all_have_name) # True
True
効率化と関連テクニック
リストのinはO(n)とパフォーマンス
計算量の目安
リストのinは最悪で先頭から末尾まで走査するためO(n)です。
データが増えるほど比例して遅くなります。
以下はデータ構造ごとのおおよその計算量の比較です。
| データ構造 | メンバーシップ平均計算量 | 備考 |
|---|---|---|
| リスト(list) | O(n) | 先頭から順に比較 |
| セット(set) | O(1) | ハッシュによる高速探索 |
| 辞書(dict) | O(1) | キー検索が高速 |
| 文字列(str) | O(n) | 部分文字列検索 |
実務上の指針
大きなデータで頻繁に判定する場合、セット化を検討してください。
頻繁な判定はset化で高速化
set による高速 membership
# リストからセットを1度だけ作って使い回すのがコツ
ids_list = list(range(100_000))
ids_set = set(ids_list) # 構築は O(n)、以降の検索は平均 O(1)
print(42 in ids_list) # O(n)
print(42 in ids_set) # O(1) 相当で高速
True
True
注意: セットは順序を保持しません。
また、リスト内の要素がリストなどの非ハッシュ可能型だとセットにできません。
if文での存在チェック
基本パターン
emails = ["a@example.com", "b@example.com"]
address = "c@example.com"
if address in emails:
print("登録済みです")
else:
print("未登録のアドレスです")
未登録のアドレスです
否定形と組み合わせる
否定条件ではnot inを用いると読みやすいです。
複数候補の判定(any・set)
any で少なくとも1つ一致
candidates = [".jpg", ".png", ".gif"]
filename = "photo.png"
if any(filename.endswith(ext) for ext in candidates):
print("画像ファイルです")
画像ファイルです
set の積集合で高速判定
required = {"read", "write"}
user_perms = ["read", "execute", "write"]
# 少なくとも1つ一致するか
print(bool(set(required) & set(user_perms))) # True
# すべて含むか(包含判定)
print(set(required).issubset(user_perms)) # True
True
True
フィルタリング(list内包表記とin)
許可リスト/禁止リストで絞り込み
allowed = {"red", "green"}
colors = ["red", "blue", "green", "green", "yellow"]
# 許可された色だけ残す
filtered = [c for c in colors if c in allowed]
print(filtered) # ["red", "green", "green"]
# 禁止リストで除外する
blocked = {"blue", "yellow"}
cleaned = [c for c in colors if c not in blocked]
print(cleaned) # ["red", "green", "green"]
['red', 'green', 'green']
['red', 'green', 'green']
index()との違いと使い分け
役割の違い
in: 含まれるかどうか(真偽)を返す。見つからなくても例外は出ない。list.index(x): 最初に見つかった位置(インデックス)を返す。xがないとValueError。
実例
items = ["a", "b", "c"]
# 位置が欲しい場合は index を使うが、例外に注意
if "b" in items: # 先に in で安全確認
pos = items.index("b") # ここは例外が出ない
print(pos) # 1
# 直接 index を使うなら try/except で囲む
try:
print(items.index("z"))
except ValueError:
print("'z' は見つかりませんでした")
1
'z' は見つかりませんでした
使い分けの目安
存在可否だけならin、位置が必要ならindex()。
存在が不確実ならinで先に確認するか、try/exceptで握りましょう。
まとめ
リストに特定要素が含まれるかはinで簡潔かつ安全に判定できます。
部分一致の誤解や型・大小文字の違い、空リストとNoneの区別など、つまずきポイントを押さえるだけでバグを大きく減らせます。
ネストや辞書ではanyや内包表記を活用し、頻繁な判定はset化で高速化しましょう。
用途に応じてinとindex()を使い分ければ、読みやすく効率的なPythonコードを書けます。
