【Python】タプル(tuple)と集合(set)の使い方|リスト・辞書との違い

【Python】タプル(tuple)と集合(set)の使い方|リスト・辞書との違い Python

Pythonには、複数の値をまとめる入れ物としてリスト(list)のほかに、タプル(tuple)集合(set)があります。タプルは「あとから変更できないリスト」、集合は「重複のない値の集まり」だと考えると分かりやすいです。座標や複数の戻り値はタプル、重複の除去やメンバー判定は集合、というように使い分けます。

つまずきやすいのは、要素が1個のタプルは末尾にカンマが必要(1,))という点と、空の集合はset()で作る{}は辞書になる)という点です。この記事では、実機のPythonで確認しながら、タプルと集合の使い方を整理します。

先に結論

  • タプルは(1, 2, 3)で作る、変更できない並びです。
  • 要素1個のタプルは(1,)とカンマを付けます((1)はただの数値)。
  • a, b = (1, 2)のようにアンパックで複数の変数へ一度に代入できます。
  • 集合は{1, 2, 3}で作る、重複のない集まりです。順序はありません。
  • 空の集合はset()で作ります。{}は辞書(dict)になります。
  • 集合は和|・積&・差-の演算や、高速なin判定が得意です。

変更できるリストはリスト(list)、キーと値で持つなら辞書(dict)、集合は内包表記と組み合わせると便利です。あわせて参考にしてください。

スポンサーリンク

タプル(tuple)の基本

タプルは丸かっこ()で作ります。リストと同じく[0]で要素を取り出したり、スライスしたりできますが、あとから中身を変更できないのが大きな違いです。

tuple-basic.py
t = (1, 2, 3)

print(t)        # (1, 2, 3)
print(t[0])     # 1(インデックスで取り出す)
print(t[-1])    # 3
print(t[0:2])   # (1, 2)(スライスもできる)
print(len(t))   # 3

# かっこは省略できる(カンマがあればタプル)
point = 10, 20
print(point)    # (10, 20)

実機でも、t[0]1、スライスt[0:2](1, 2)が取り出せました。取り出し方はリストと同じです。point = 10, 20のようにかっこを省略しても、カンマがあればタプルになります。

要素1個のタプルはカンマが必要

ここはよく間違える点です。要素が1個のタプルを作るときは、末尾にカンマを付けて(1,)と書きます。カンマがない(1)は、かっこで囲んだだけの「ただの数値」になってしまいます。

tuple-single.py
a = (1)     # これはタプルではない(ただの int)
b = (1,)    # これがタプル(末尾にカンマ)

print(type(a))   # <class 'int'>
print(type(b))   # <class 'tuple'>
print(b)         # (1,)
(1) はタプルではない

実機で確認したところ、(1)の型はint(1,)の型はtupleでした。かっこはタプルの目印ではなく、カンマがタプルの目印です。要素が1個のタプルを作りたいのにカンマを忘れると、タプルにならずにあとの処理でエラーになることがあります。1個のときは必ず(1,)と書きましょう。

タプルは変更できない(immutable)

タプルはイミュータブル(変更不可)です。要素を後から書き換えたり、追加・削除したりはできません。試みるとTypeErrorになります。

tuple-immutable.py
t = (1, 2, 3)

# 要素を変更しようとするとエラー
t[0] = 99
# TypeError: 'tuple' object does not support item assignment

# 変更したいときは、いったんリストにする
lst = list(t)      # [1, 2, 3]
lst[0] = 99
t = tuple(lst)     # (99, 2, 3) に作り直す
print(t)

実機でも、t[0] = 99とするとTypeError: 'tuple' object does not support item assignmentになりました。中身を変えたい場合は、list(t)でリストに変換してから書き換え、必要ならtuple()でタプルに戻します。「変更されたくないデータ」をタプルにしておくと、うっかり書き換えるミスを防げます。

タプルのアンパックと複数の戻り値

タプルはアンパックと相性が良く、複数の変数へ一度に代入できます。関数で複数の値を返すときも、実体はタプルです。

tuple-unpack.py
# アンパック(左辺の変数へ一度に代入)
a, b, c = (1, 2, 3)
print(a, b, c)   # 1 2 3

# 値の入れ替えも一行で書ける
x, y = 10, 20
x, y = y, x
print(x, y)      # 20 10

# 関数の「複数の戻り値」は実はタプル
def minmax(numbers):
    return min(numbers), max(numbers)

result = minmax([3, 1, 2])
print(result)        # (1, 3)
low, high = minmax([3, 1, 2])   # 受け取りもアンパック
print(low, high)     # 1 3

実機でも、minmax([3,1,2])の戻り値は(1, 3)というタプルでした。low, high = minmax(...)のようにアンパックで受け取れます。x, y = y, xと書くだけで2つの変数の値を入れ替えられるのも、タプルのアンパックの便利な使い方です。

集合(set)の基本と重複除去

集合は波かっこ{}で作る、重複のない値の集まりです。同じ値を入れても1つにまとめられます。リストから重複を取り除きたいときによく使います。

set-basic.py
s = {3, 1, 2, 2, 1}
print(s)    # {1, 2, 3}(重複は除かれる・順序は保証されない)

# リストの重複を取り除く定番の方法
nums = [1, 1, 2, 3, 3, 3]
unique = list(set(nums))
print(unique)   # [1, 2, 3]

実機でも、{3, 1, 2, 2, 1}{1, 2, 3}になり、重複が除かれました。list(set(nums))はリストの重複除去の定番です。ただし集合には順序がありません。元の並び順を保ちたい場合は、別の方法(dict.fromkeysなど)を使います。

空の集合はset()({}は辞書)

もう1つの注意点です。空の集合を作りたいとき、{}と書くと空の辞書(dict)になってしまいます。空の集合はset()で作ります。

set-empty.py
empty_dict = {}        # これは空の辞書(dict)
empty_set = set()      # これが空の集合(set)

print(type(empty_dict))   # <class 'dict'>
print(type(empty_set))    # <class 'set'>
空の {} は集合ではなく辞書

実機で確認したところ、{}の型はdictset()の型はsetでした。波かっこ{}は辞書と集合のどちらにも使う記号ですが、空のときは辞書が優先されます。{1, 2}のように要素があれば集合になりますが、空の集合がほしいときは必ずset()と書いてください。

集合の演算(和・積・差)

集合は、数学の集合と同じように和集合・積集合・差集合を計算できます。2つのデータの共通点や違いを調べるのに便利です。

set-ops.py
A = {1, 2, 3}
B = {2, 3, 4}

print(A | B)   # {1, 2, 3, 4}  和集合(どちらかにある)
print(A & B)   # {2, 3}        積集合(両方にある)
print(A - B)   # {1}           差集合(Aにだけある)
print(A ^ B)   # {1, 4}        対称差(どちらか片方だけにある)

# メソッドでも書ける
print(A.union(B))          # {1, 2, 3, 4}
print(A.intersection(B))   # {2, 3}

実機でも、A | B{1, 2, 3, 4}A & B{2, 3}A - B{1}になりました。「両方のリストに共通する要素」を求めたいときは積集合&、「片方にしかない要素」は差集合-が便利です。記号のほか、union()intersection()といったメソッドでも同じことができます。

要素の追加・削除とin判定

集合にはadd()で要素を追加、discard()remove()で削除します。また、ある値が含まれるかのin判定が、リストより高速なのも集合の特長です。

set-add-in.py
s = {1, 2, 3}

s.add(5)        # 追加 → {1, 2, 3, 5}
s.discard(1)    # 削除 → {2, 3, 5}(無くてもエラーにならない)
# s.remove(9)   # remove は無いとエラー(KeyError)

print(s)        # {2, 3, 5}

# in 判定(集合は高速)
print(2 in s)   # True
print(9 in s)   # False
たくさんのin判定は集合が速い

「この値はリストの中にあるか?」という判定(in)を何度も行う場合、リストよりも集合のほうが高速です。リストは先頭から順に探すのに対し、集合は仕組み上ほぼ一定の速さで判定できるためです。大量のデータに対して何度も含まれるかを確認するなら、いったんset()に変換してからinで調べると効率的です。なおdiscard()は要素が無くてもエラーになりませんが、remove()は無いとKeyErrorになる点が違います。

リスト・辞書との使い分け

4つの入れ物の特徴をまとめます。目的に合わせて選びます。

種類 書き方 特徴・主な用途
リスト list [1, 2, 3] 順序あり・変更できる。並びを扱う基本
タプル tuple (1, 2, 3) 順序あり・変更できない。座標・複数の戻り値
集合 set {1, 2, 3} 重複なし・順序なし。重複除去・集合演算・in判定
辞書 dict {"a": 1} キーと値の対応。名前で値を引く

「変更したくない並び」はタプル、「重複をなくしたい・共通点を調べたい」は集合、「キーで値を引きたい」は辞書、それ以外の基本はリスト、と考えると選びやすくなります。

よくある失敗

要素1個のタプルでカンマを忘れる

(1)はただの数値です。1個のタプルは(1,)とカンマを付けます。

空の集合を{}で作ってしまう

{}は空の辞書です。空の集合はset()で作ります。

タプルの要素を書き換えようとする

タプルは変更できません。t[0] = xはエラーです。変更したいときはlist()に変換します。

集合に順序を期待する

集合には順序がありません。set[0]のようなインデックス指定もできません。順序が必要ならリストを使います。

remove()で無い要素を消そうとする

remove()は無い要素を指定するとKeyErrorになります。エラーにしたくないときはdiscard()を使います。

よくある質問

Qタプルとリストはどう使い分けますか?
Aあとから中身を変えたいならリスト、変えたくないならタプルです。タプルは変更できないため、座標(x, y)のような「セットで意味を持つ変わらないデータ」や、関数の複数の戻り値に向いています。リストは要素の追加・削除・並べ替えができる、並びを扱う基本の入れ物です。
Q要素が1個のタプルの書き方は?
A末尾にカンマを付けて(1,)と書きます。(1)はかっこで囲んだだけの数値になってしまいます。タプルの目印はかっこではなくカンマです。
Qリストの重複を取り除くには?
Alist(set(リスト))が簡単です。ただし集合には順序が無いため、元の並び順が保証されません。順序を保ったまま重複を消したいときはlist(dict.fromkeys(リスト))を使います。
Q空の集合はどう作りますか?
Aset()で作ります。{}は空の辞書(dict)になるため、空の集合には使えません。要素がある場合は{1, 2, 3}で集合になります。
Q2つのリストの共通要素を求めるには?
Aそれぞれを集合にして積集合をとります。set(リストA) & set(リストB)で、両方に含まれる要素が得られます。片方にしかない要素は差集合set(A) - set(B)で求められます。

まとめ

  • タプルは(1, 2, 3)で作る変更できない並び。1個のときは(1,)
  • タプルはアンパック(a, b = t)や複数の戻り値で活躍します。
  • 集合は{1, 2, 3}で作る重複なしの集まり。list(set(...))で重複除去。
  • 空の集合はset(){}は辞書になります。
  • 集合は和|・積&・差-の演算と、高速なin判定が得意です。
  • 変更不可ならタプル、重複除去・集合演算なら集合、と使い分けます。

タプルと集合は、リストや辞書と並ぶPythonの基本データ型です。「変更させたくないからタプル」「重複をなくしたいから集合」と、目的に合わせて選べるようになると、コードがぐっと読みやすく・安全になります。