TypeError: unhashable type: 'list' は、list を dict のキーや set の要素にしようとしたときのエラーです。 dict のキーと set の要素には、中身が変わらない値(ハッシュ可能な値)しか使えません。list・dict・set は中身を変えられるので使えません。

エラー文の型 置き換える型
'list' tuple(tuple(リスト))
'set' frozenset
'dict' frozenset(d.items())、または JSON 文字列
自作のクラス(dataclass など) @dataclass(frozen=True)

確認環境:Python 3.11、pandas 2.2.3・3.0.6(確認日 2026年10月11日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「unhashable type: 'list'の原因と直し方|Python」

再現:エラーになる書き方

書き方 結果
{["東京", "大阪"]: 1}(dict のキーに list) unhashable type: 'list'
{[1, 2], [3]}(set に list) unhashable type: 'list'
set([[1, 2], [1, 2]])(リストのリストの重複を消す) unhashable type: 'list'
[1, 2] in {(1, 2)}(set に list があるか調べる) unhashable type: 'list'
{{"id": 1}: "x"}(dict のキーに dict) unhashable type: 'dict'
{{1, 2}}(set の中に set) unhashable type: 'set'
@lru_cache の関数に list を渡す unhashable type: 'list'
@dataclass のオブジェクトを set に入れる unhashable type: 'P'
{(1, [2, 3]): "x"}(中に list がある tuple) unhashable type: 'list'

in で調べるだけでもエラーになります。 set や dict を調べるときも、内部でハッシュ値を計算するためです。

なぜ list はキーにできないのか

dict や set は、値から計算したハッシュ値で、場所を決めて保存しています。もし list をキーにして、あとで中身を変えると、ハッシュ値が変わり、保存した場所から見つけられなくなります。そのため Python は、中身を変えられる list・dict・set をキーに使わせません。

Python 公式の用語集でも、「変更できないほとんどの組み込みの型はハッシュ可能、変更できるコンテナ(list・dict)はハッシュ可能でない。tuple・frozenset は、中身がすべてハッシュ可能なときだけハッシュ可能」とされています。表の最後の (1, [2, 3]) がエラーになったのはこのためです。

直し方1:list → tuple

counts = {}
key = tuple(["東京", "大阪"])           # ('東京', '大阪')
counts[key] = 1                         # OK

リストのリストの重複を消すなら、tuple にしてから dict.fromkeys で、順番を保ったまま消せます。

rows = [[1, 2], [3], [1, 2]]
unique = list(dict.fromkeys(tuple(r) for r in rows))
# [(1, 2), (3,)]

中に list が入った list([[1, [2, 3]]])は、内側も tuple にする必要があります。

直し方2:set → frozenset、dict → frozenset または JSON 文字列

groups = {frozenset({1, 2})}                       # set の中に set を入れたいとき

# dict をキーにしたいとき(並び順に関係なく同じ dict を同じキーにする)
key1 = frozenset({"id": 1, "a": 2}.items())
key2 = json.dumps({"id": 1, "a": 2}, sort_keys=True)  # '{"a": 2, "id": 1}'
方法 向いている場面
frozenset(d.items()) 値がすべてハッシュ可能(文字列・数値)なとき
json.dumps(d, sort_keys=True) 値に list などが入っているとき。sort_keys=True で並び順をそろえる

直し方3:lru_cache には tuple で渡す

@functools.lru_cache
def total(items):
    return sum(items)

total([1, 2, 3])        # TypeError: unhashable type: 'list'
total((1, 2, 3))        # 6

lru_cache は、引数をキーにして結果を覚えるため、引数もハッシュ可能である必要があります。呼び出し側で tuple にして渡します。

直し方4:dataclass は frozen=True

@dataclass
class P:
    x: int

{P(1)}                 # TypeError: unhashable type: 'P'

@dataclass(frozen=True)
class F:
    x: int

{F(1), F(1)}           # {F(x=1)}    ← 同じ値は1つにまとまる

用語集では「自作のクラスのオブジェクトは、既定でハッシュ可能」とされていますが、@dataclass は既定で == での比較を作るため、ハッシュ可能でなくなりました(unhashable type: 'P')。frozen=True にすると、値を変えられない代わりに、set や dict のキーに使えます。

pandas で出るとき

列の値が list(タグ・選択肢の一覧など)だと、pandas の操作で同じエラーになります。

df = pd.DataFrame({"name": ["佐藤", "鈴木", "佐藤"],
                   "tags": [["A", "B"], ["C"], ["A", "B"]],
                   "score": [1, 2, 3]})
操作 pandas 2.2.3・3.0.6
df.drop_duplicates() unhashable type: 'list'
df.groupby("tags") unhashable type: 'list'
df["tags"].value_counts() 動く([A, B] が 2、[C] が 1)
df.drop_duplicates(subset=["name"]) 動く(list の列を比べないため)
# list の列を tuple にしてから
df2 = df.assign(tags=df["tags"].apply(tuple))
df2.drop_duplicates(subset=["name", "tags"])

# タグごとに数えたいなら、行を分けてから
df["tags"].explode().value_counts()
# A 2 / B 2 / C 1

explode() は、list の要素ごとに行を分けます。 「タグ A が付いた件数」のように、list の中身で集計したいときに使います。

似たエラー:set を列の指定に使った

df[{"name"}]
# TypeError: Passing a set as an indexer is not supported. Use a list instead.

列を選ぶときは、set({ })ではなく list([ ])で指定します。

確認の手順(チェックリスト)

順番 確認すること 方法
1 どの型か エラー文の 'list'・'dict'・'set'・クラス名
2 どこでキー・set に使っているか dict のキー、set、in、lru_cache、pandas の groupby・drop_duplicates
3 置き換える list→tuple、set→frozenset、dict→frozenset・JSON 文字列
4 tuple の中も確かめる 中に list があると、tuple でもエラー
5 自作のクラス @dataclass(frozen=True)
6 pandas list の列を tuple に、または explode()

よくある質問

Q. tuple に変えたら、あとで中身を変えられません。 A. キーとして使う値は、変えないのが前提です。中身を変えたいデータは list のまま値の側に持ち、キーには ID や tuple を使います。

Q. JSON から読み込んだデータで出ます。 A. JSON の配列は list、オブジェクトは dict として読み込まれます。それをキーや set に入れるときは、上の方法で tuple・JSON 文字列などに変えます。

まとめ

  • list・dict・set は中身を変えられるので、dict のキーや set の要素にできない
  • list → tuple、set → frozenset、dict → frozenset(d.items()) か JSON 文字列
  • tuple でも、中に list があるとエラー
  • in での確認・lru_cache の引数・@dataclass(frozen なし)でも出る
  • pandas は list の列で drop_duplicates・groupby が失敗。tuple にするか explode()

参考・出典

確認日はいずれも 2026年10月11日です。

  • Python ドキュメント「Glossary」(hashable):https://docs.python.org/3/glossary.html#term-hashable

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「unhashable type: 'list'の原因と直し方|Python」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?