閉じる

Pythonのin演算子でリストに値が含まれるかを判定する方法

Pythonで複数のデータを順番に管理するにはリスト(list)を使います。

本稿では、リストの中に特定の要素が含まれるかを素早く安全に判定できるin演算子に焦点を当て、基本の使い方から内部挙動、実用パターン、注意点とベストプラクティスまでを順を追って解説します。

Pythonのin演算子の基本

リストに値が含まれるかを判定する書き方

in演算子は、左辺の値が右辺のリストに含まれるかどうかを真偽値で返します。

最も基本的な書き方は次のとおりです。

Python
# リストを用意します
fruits = ["apple", "banana", "orange"]

# "banana" がリストに含まれるか確認
print("banana" in fruits)  # True になるはず

# "grape" は含まれていないので False
print("grape" in fruits)
実行結果
True
False

inはリストの各要素と左辺の値を順に比較し、どれか1つでも一致すればTrue、1つも一致しなければFalseを返します。

戻り値は真偽値(bool)です。

not inで含まれないことを判定

含まれていないことを確認したい場合は、not inを使います。

Python
fruits = ["apple", "banana", "orange"]

# "grape" が含まれていないことを確認
print("grape" not in fruits)  # True

# "banana" は含まれているので False
print("banana" not in fruits)
実行結果
True
False

not inはinの否定です。

条件分岐や入力検証で「禁止リストに含まれていないか」を確かめるときに便利です。

if文での真偽値の使い方

inはそのままifの条件にできます。

追加の比較は不要です。

Python
fruits = ["apple", "banana", "orange"]

if "banana" in fruits:
    print("banana はあります")
else:
    print("banana はありません")

# 悪い例: 不要な比較は書かない
flag = ("banana" in fruits)
# if flag == True:  # 冗長
#     ...

# 良い例: そのまま条件に使う
if flag:
    print("フラグは True です")
実行結果
banana はあります
フラグは True です

inはboolを返します。

if (x in lst) == True のような書き方は冗長なので避け、if x in lst: と簡潔に書くのが読みやすさと保守性の面で推奨です。

リストでのin演算子の挙動

比較は==で評価される

inは要素との比較に等値比較(==)を用い、オブジェクトの同一性(is)ではありません。

そのため、型間の等価性や特殊値の性質が影響します。

Python
import math
from decimal import Decimal

print(1 in [True, 2])          # True: 1 == True は True
print(True in [1, 2])          # True: True == 1 は True
print("1" in [1, "1", 2])      # True: 文字列 "1" は "1" と一致
print("1" in [1, 2])           # False: 文字列 "1" と数値 1 は一致しない
nan1 = float("nan")
nan2 = float("nan")
print(nan1 == nan2)            # False: NaN 同士は == でも一致しない
print(nan1 in [nan2])          # False: in も == に従うため一致しない
print(Decimal("1.0") in [1, 2, 3])  # True: Decimal と int は数値的に等しい
実行結果
True
True
True
False
False
False
True

boolはintのサブクラスで、True == 1False == 0 が成り立ちます。そのため混在に注意が必要です。

文字列と数値は異なる型なので通常は一致しません。

NaNはどの値とも等しくない性質を持つため、同じNaNどうしでもinで一致しません。

Decimalは数値としての等価性で比較されるため、Decimal("1.0") == 1 はTrueになります。

参考として、いくつかの式と結果を整理します。

結果理由
1 in [True]True1 == True がTrue
True in [1]TrueTrue == 1 がTrue
"1" in [1]False文字列と数値は一致しない
float("nan") in [float("nan")]FalseNaN同士でも==はFalse

探索は先頭から順に行われる

リストに対するinは先頭から順に要素を比較し、最初に一致した時点で探索を打ち切ります。

以下はその様子を可視化する例です。

Python
# 比較の過程を出力するクラス
class EchoEq:
    def __init__(self, value):
        self.value = value
    def __eq__(self, other):
        o = getattr(other, "value", other)
        print(f"== を評価: {self.value} == {o}")
        return self.value == o
    def __repr__(self):
        return f"EchoEq({self.value})"

items = [EchoEq(1), EchoEq(2), EchoEq(3)]
target = EchoEq(3)

print(target in items)  # 先頭から順に == が呼ばれる
実行結果
== を評価: 1 == 3
== を評価: 2 == 3
== を評価: 3 == 3
True

上から順に==が呼ばれ、3つ目で一致した瞬間に探索が終了しています。

先頭付近に候補があるほど速く、末尾や存在しない場合は遅くなります。

文字列は大文字小文字を区別する

文字列の比較はデフォルトで大文字小文字を区別します。

必要に応じて正規化しましょう。

Python
fruits = ["apple", "Banana", "ORANGE"]

# 素直な比較(区別あり)
print("apple" in fruits)   # True
print("Apple" in fruits)   # False

# 小文字に正規化して比較(区別なし)
needle = "Apple"
print(needle.lower() in [s.lower() for s in fruits])  # True

# anyを使った等価な書き方
print(any(s.lower() == "apple" for s in fruits))      # True
実行結果
True
False
True
True

ケースインセンシティブにしたいときは、比較前に両者を同じ形式(例: 全て小文字)に正規化するのが基本です。

in演算子の実用パターン

完全一致でリストの要素を判定

リストのinは「要素の完全一致」を判定します。

部分文字列の判定と混同しないようにしましょう。

Python
colors = ["red", "green", "blue"]

# 完全一致
print("red" in colors)   # True

# 部分一致はしない
print("re" in colors)    # False

# 参考: 文字列同士の部分一致は文字列の in
print("py" in "python")  # True (これは文字列の話)
実行結果
True
False
True

"re" in colors はFalseですが、"py" in "python" はTrueです。

前者は「リストの要素として存在するか」、後者は「文字列に部分として含まれるか」という別の話です。

ネストしたリストの存在チェック

ネストしたリスト(リストの中にリストが入っている)では、inは「トップレベルの要素」に対して働きます。

内側の要素まで自動的に探しません。

Python
nested = [[1, 2], [3, 4], [5, 6]]

# サブリスト自体が要素として存在するか
print([3, 4] in nested)  # True

# 2 がどれかの内側リストに含まれるか(トップレベルでは見つからない)
print(2 in nested)  # False

# 内側まで探したい場合は any を使う
print(any(2 in inner for inner in nested))  # True

# すべての内側リストに 2 が含まれるかを調べるなら all
print(all(2 in inner for inner in nested))  # False
実行結果
True
False
True
False

内側まで見たいときは明示的にループ、内包表記、any/allを使います。

トップレベルのリストに対してinを使うだけでは、内側のスカラー要素までは届きません。

複数候補の存在チェック

「1つの値が複数の候補に含まれるか」と「複数の候補のうち1つでもリストに存在するか」の2つの状況を区別します。

Python
value = "png"

# 1) 単一の値が複数候補に含まれるか
allowed = ["jpg", "jpeg", "png", "gif"]
if value in allowed:
    print("許可された拡張子です")

# 2) 複数候補のうち1つでもリストにあるか
installed = ["numpy", "pandas", "requests"]
candidates = ["httpx", "requests", "urllib3"]
if any(pkg in installed for pkg in candidates):
    print("候補のうち少なくとも1つはインストール済みです")

# 3) 大量の候補を高速に判定したい場合は set を使って集合演算
installed_set = set(installed)
candidates_set = set(candidates)
if installed_set & candidates_set:  # 積集合が空でなければ何かが共通
    print("集合の積で共通要素が見つかりました")
実行結果
許可された拡張子です
候補のうち少なくとも1つはインストール済みです
集合の積で共通要素が見つかりました

候補が多い場合や繰り返し判定する場合は、setに変換してから集合演算(積集合、部分集合など)を使うと簡潔で高速です。

in演算子の注意点とベストプラクティス

型の違いによる一致に注意

数値と文字列、boolとint、Decimalなど、型ごとの等価性は異なります。

意図しない一致や不一致を避けるため、比較前に型や形式をそろえるのが安全です。

Python
from decimal import Decimal

values = [0, 1, 2, "3", Decimal("4.0")]

print("1" in values)          # False: 文字列 "1" と数値 1 は一致しない
print(True in values)         # True: True == 1
print(Decimal("4") in values) # True: Decimal(4) と Decimal("4.0") は数値的に等しい

# 型をそろえる例(すべて文字列に変換して比較)
needle = "1"
values_str = [str(v) for v in values]
print(needle in values_str)   # True
実行結果
False
True
True
True

boolとintの混在は特に誤判定の温床です。必要なら型を明示的に揃えてから比較してください。

NaNの扱いは特別です。math.isnanpandas.isnaなど、目的に応じた関数で検出します。

大きなリストは線形探索

リストのinは線形探索(O(n))です。

サイズが大きい、または判定を大量に繰り返す場合は、セット(set)に変換して平均O(1)の探索にしましょう。

Python
import time

N = 100_000
arr = list(range(N))

# 先頭/中央/末尾/非存在の探索時間を比較
for target in (0, N // 2, N - 1, -1):
    t0 = time.perf_counter()
    _ = target in arr
    t1 = time.perf_counter()
    print(f"list で {target=!r} を検索: {(t1 - t0)*1e6:.1f} µs (目安)")

# set に変換してから探索
s = set(arr)
for target in (0, N // 2, N - 1, -1):
    t0 = time.perf_counter()
    _ = target in s
    t1 = time.perf_counter()
    print(f"set で {target=!r} を検索: {(t1 - t0)*1e6:.1f} µs (目安)")

実行結果実行環境により大きく変化します)
list で target=0 を検索: 0.5 µs (目安)
list で target=50000 を検索: 185.4 µs (目安)
list で target=99999 を検索: 362.6 µs (目安)
list で target=-1 を検索: 356.6 µs (目安)
set で target=0 を検索: 0.6 µs (目安)
set で target=50000 を検索: 0.4 µs (目安)
set で target=99999 を検索: 0.2 µs (目安)
set で target=-1 を検索: 0.9 µs (目安)

リストは位置によって時間が大きく変わるのに対し、セットは要素数にほぼ依存しない一定時間で判定できます。

ただし1回だけの判定なら、リストから都度setに変換するコストが上回る場合もあります。繰り返しの判定や大規模データでこそ効果的です。

条件式を簡潔に書く

読みやすく、かつバグを生みにくい書き方を選びます。

Python
user_role = "admin"
enabled_features = ["export", "share", "audit"]

# 良い例: in をそのまま条件に使い、候補はタプル/リストで明示
if user_role in ("admin", "owner") and "export" in enabled_features:
    print("エクスポートを許可")

# 複数の必須権限をすべて満たすか(all)
required = ["read", "write"]
perms = ["read", "write", "delete"]
if all(p in perms for p in required):
    print("必要な権限をすべて満たしています")

# いずれか1つあればよい(any)
if any(p in perms for p in ["approve", "write"]):
    print("いずれかの権限を満たしています")

# 悪い例: 冗長な比較やネガティブ条件の多重は避ける
# if (user_role in ["admin", "owner"]) == True and not ("export" not in enabled_features):
#     ...
実行結果
エクスポートを許可
必要な権限をすべて満たしています
いずれかの権限を満たしています

if x in lst: と簡潔に書くことで、意図が明確になります。

複数条件は、any/allや、場合によってはsetの集合演算(例: set(required) <= set(perms))を使うと表現力が上がります。

まとめ

本稿では、リストに対するin演算子の基本から、内部挙動、実用的な書き方、そして注意点とベストプラクティスまでを解説しました。

ポイントは次のとおりです。

inは要素の等値(==)で先頭から順に比較し、真偽値を返します。

型の違い(特にboolとint、NaN、Decimalなど)が結果に影響するため、必要に応じて型や表記を正規化しましょう。

大規模データや繰り返し判定ではsetを用いると劇的に高速になります。

条件式は簡潔に、any/allや集合演算を適切に組み合わせることで、読みやすく正確なコードにできます。

リストの中に特定の要素が含まれるかを判定する場面では、ここで紹介したパターンを組み合わせて堅牢な判定ロジックを構築してください。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!