当サイトは、アフィリエイト広告を利用しています

【Python】list・set・tupleの違いを整理する

作成日:2026月02月15日
更新日:2026年02月15日

Pythonで複数の値を扱う際は

  • list
  • set
  • tuple

の3つを扱うことになる

いずれもコレクション型だが、内部構造・計算量・用途は明確に異なる。

本記事では

  • 構造的な違い
  • 基本操作
  • 実務での使い分け

を体系的に整理する。

list

listの特徴としては

  • 順序がある
  • 可変である
  • 要素重複可能
  • インデックスアクセス可能
  • 内部構造は動的配列

がある。

そのため、用途としては

  • 順序が重要な場合
  • 要素重複が重要な場合
  • 頻繁に変更がある場合

を想定している。

計算量

listの存在判定は線形探索(O(n))である。
つまり、 頭から順に走査する必要がある。

操作

作成から追加、削除などの基本的な操作をまとめておく。

作成

list作成
# リテラル
lst:str = []
lst:str = ["A", "B", "C"]
# listコンストラクタ
lst = list()
lst = list([1, 2, 3])
lst = list("abc") # ['a', 'b', 'c']
# 他のイテラブルから変換するとき
tuple_data = (1, 2, 3)
lst = list(tuple_data)
# リスト内包表記(実務で多用)
lst = [i for i in range(5)]

色々作り方がある。

  • 普通に作る時はリテラルを使う
  • 他のイテラブルから変換する時はコンストラクタを使う
  • 抽出する時などの条件付きで作成する場合はリスト内包表記を使う

追加

list追加
# 作成
lst = [1, 2, 3] # [1, 2, 3]
# 末尾に追加
lst.append(4) # [1, 2, 3, 4] # 追加
# 末尾に複数追加
# イテラブルを展開して追加される
lst.extend([5, 6, 7]) # [1, 2, 3, 4, 5, 6, 7] # 追加
# appendでやった場合
lst.append([5, 6, 7]) # [1, 2, 3, 4, [5, 6, 7]] # 追加
# 指定インデックスに追加
# 第一引数はインデックス、第二引数は追加する値
lst.insert(2, 10) # [1, 2, 10, 3, 4, 5, 6, 7] # 追加
# スライス代入(値)
lst[2:2] = [10] # [1, 2, 10, 3, 4, 5, 6, 7] # 追加
# スライス代入(イテラブル)
# lst[2:2] は「空スライス」
# そこにイテラブルを代入すると
# その位置に展開挿入される
lst[2:2] = [10, 20] # [1, 2, 10, 20, 3, 4, 5, 6, 7] # 追加

取得

list取得
# 作成
lst = [1, 2, 3] # [1, 2, 3]
# 末尾の要素を取得
lst[-1] # 3
# 指定インデックスの要素を取得
lst[1] # 2
# スライス
# sequence[start:stop:step]
# start:開始インデックス(含む)
# stop:終了インデックス(含まない)
# step:間隔(省略可)
# インデックス範囲を指定して取得(スライス)
lst[1:3] # [2, 3]
# インデックス範囲を指定して取得(スライス)
lst[:3] # [1, 2, 3]
# インデックス範囲を指定して取得(スライス)
lst[1:] # [2, 3]

削除

list削除
# 作成
lst = [1, 2, 3] # [1, 2, 3]
# 末尾の要素を削除
del lst[-1] # [1, 2]
# 指定インデックスの要素を削除
del lst[1] # [1, 3]
# スライス削除
# sequence[start:stop:step]
# start:開始インデックス(含む)
# stop:終了インデックス(含まない)
# step:間隔(省略可)
del lst[1:3] # [1, 3]
# インデックスではなく、値を指定して削除する
# 最初に見つかった値を削除
# 見つからない場合はエラー(valueError)
lst.remove(3) # [1, 2]
# 末尾の要素を取り出す
# 取り出すとなくなる
lst.pop() # [1, 2]
# 要素をインデックスで取り出す
# 取り出すとなくなる
lst.pop(0) # [2]

set

setの特徴としては

  • 順序なし
  • 要素重複不可
  • 可変である
  • インデックスアクセス不可
  • 内部構造はハッシュテーブル
  • 要素はhashable必須

がある。

用途は

  • 大量データの高速存在判定
  • 集合演算
  • 重複排除

が想定される

計算量

平均O(1)になる。

内部構造はハッシュテーブルとは?

setはハッシュ値を計算して値を格納する。
そのため、listなどの内部構造が動的配列なものに比べると
高速な操作が可能になる。

要素はhashable必須

hashableとは、
hash()を呼び出すことが可能なオブジェクトを指す。
要はハッシュ値が計算できて、かつ不変であるオブジェクトのこと。
※変更によってハッシュ値が変わらないこと

具体的には

  • int
  • float
  • str
  • tuple
  • frozenset
  • boolean
  • None

が該当する

逆にhashableでなのは

  • list
  • dict
  • set

が該当する。
理由は可変でだから

なぜ可変だとダメなのか

hashu
# list作成
x = [1,2]
# listをsetに入れる
s = {x}
# listに要素を追加
x.append(3)

これを実行すると
TypeError: unhashable type: 'list'というエラーが出る。

理由は元のハッシュ値と内容が一致しなくなるから

だから Python は最初から禁止している。

操作

作成から追加、削除、集合演算などの基本的な操作をまとめておく。

作成

set操作
# 作成
# リテラル
s = {1, 2, 3} # {1, 2, 3}
# コンストラクタで作成
# listからsetに変換
s = set([1, 2, 3]) # {1, 2, 3}
# set内包表記
s = {i for i in range(5)} # {0, 1, 2, 3, 4}

追加

追加
# 作成
s = {1, 2, 3}
# 追加※単数のみ
s.add(4) # {1, 2, 3, 4}
# 追加(複数)
# イテラブルを展開して追加される※内部的には1要素ずつ、addしている
s.update([5, 6]) # {1, 2, 3, 4, 5, 6}

取得

取得
# 作成
s1 = {1, 2, 3}
# 取得(ループ)
for n in s1:
print(n) # 1, 2, 3
# 取得(リスト化)
l = list(s1) # [1, 2, 3]
# ランダムで1つ取得
l = s1.pop() #
# 条件で取り出す(set内包表記)
s = {x for x in s1 if x % 2 == 0} # {2}
  • インデックス指定はできない(s[0] は不可)
  • 1つ取り出すなら pop()(どれが出るか不定・削除される)
  • 条件で取り出すなら内包表記(set内包表記)を使う

set は順序を持たないためインデックスでの取得ができず、
また特定の値を「取り出す」操作も基本的には削除を伴うため、
位置を基準に柔軟に扱える listと比べると取り出しの自由度は低く、扱いづらく感じる場合がある。

削除

削除
# 作成
s1 = {1, 2, 3}
# 値を指定して削除
# 値が存在しない場合はkeyError
s1.remove(2) # {1, 3}
# 値がなくても
s1.discard(4) # {1, 3}
# 全削除
s1.clear() # set()

集合演算|和集合

条件でいうとORをとるのと同等

和集合
# 作成
s1 = {1, 2, 3}
s2 = {3, 4, 5}
# 和集合
s3 = s1 | s2 # {1, 2, 3, 4, 5}
# または
s3 = s1.union(s2) # {1, 2, 3, 4, 5}
# 破壊的な和集合
s1 |= s2 # {1, 2, 3, 4, 5}
# または
s1.update(s2) # {1, 2, 3, 4, 5}
  • |演算子はunionメソッドを呼び出す
  • |=演算子はupdateメソッドを呼び出す
  • 破壊的とは、元のオブジェクトに直接変更を加えることを意味する

集合演算|差集合

これは引き算してるイメージ

差集合
# 作成
s1 = {1, 2, 3}
s2 = {3, 4, 5}
# 差集合
s3 = s1 - s2 # {1, 2}
# または
s3 = s1.difference(s2) # {1, 2}
# 破壊的な差集合
s1 -= s2 # {1, 2}
# または
s1.difference_update(s2) # {1, 2}
  • -演算子はdifferenceメソッドを呼び出す
  • -=演算子はdifference_updateメソッドを呼び出す

集合演算|積集合

条件でいうとANDをとるのと同等

積集合
# 作成
s1 = {1, 2, 3}
s2 = {3, 4, 5}
# 積集合
s3 = s1 & s2 # {3}
# または
s3 = s1.intersection(s2) # {3}
# 破壊的な積集合
s1 &= s2 # {3}
# または
s1.intersection_update(s2) # {3}
  • &演算子はintersectionメソッドを呼び出す
  • &=演算子はintersection_updateメソッドを呼び出す

集合演算|対称差集合

両方にある値だけを除外する

対称差集合
# 作成
s1 = {1, 2, 3}
s2 = {3, 4, 5}
# 対称差集合
s3 = s1 ^ s2 # {1, 2, 4, 5}
# または
s3 = s1.symmetric_difference(s2) # {1, 2, 4, 5}
# 破壊的な対称差集合
s1 ^= s2 # {1, 2, 4, 5}
# または
s1.symmetric_difference_update(s2) # {1, 2, 4, 5}
  • ^演算子はsymmetric_differenceメソッドを呼び出す
  • ^=演算子はsymmetric_difference_updateメソッドを呼び出す

tuple

tupleの特徴としては

  • 順序がある
  • 不変である(変更不可)
  • 要素重複可能
  • インデックスアクセス可能
  • 変更不可なので安全性が高い
  • hash可能(要素がhashableであれば)

がある。

そのため、用途としては

  • 変更されないデータ
  • 定数データ
  • 関数の戻り値
  • dictのキー

を想定している。

操作

作成、取り出し操作をまとめておく。

作成

tuple作成
# リテラル
tup = (1, 2, 3) # (1, 2, 3)
# コンストラクタ
tup = tuple([1, 2, 3]) # (1, 2, 3)
tup = tuple('Hello') # ('H', 'e', 'l', 'l', 'o')
# 関数
tup = tuple(range(10)) # (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

取得

tuple取得
# リテラル
tup = (1, 2, 3) # (1, 2, 3)
# インデックス
tup[0] # 1
tup[-1] # 3
# スライス
tup[1:3] # (2, 3)
tup[1:] # (2, 3)
tup[:2] # (1, 2)
tup[:] # (1, 2, 3)
# アンパック
a, b, c = tup # a = 1, b = 2, c = 3

まとめ

簡潔にまとめると

  • listは「可変な配列」
  • setは「高速な集合」
  • tuple は「不変な配列」

重要なのは、 データの性質(順序・変更有無・重複・判定頻度)から逆算して型を選ぶこと。

なので設計観点での型選択基準として

  • 順序が必要か?
  • 重複を許容するか?
  • 変更されるか?
  • 高速な存在判定が必要か?

を設計段階で考える必要がある。

日常的にはlistを目にする場面が多いが、要件によっては set や tuple が中心になることもある。
「とりあえずlist」で始めて、用途が見えた段階で型を確定させるのも現実的かな。

関連記事

新着記事

タグ一覧
top