Pythonで複数のデータをまとめて扱うときによく使うのが、「リスト(list)」「タプル(tuple)」「セット(set)」です。書き方は []、()、{} とカッコが違うだけに見えますが、順番、重複、書き換えられるかどうかがそれぞれ違います。
C言語でいえば、リストは大きさを変えられる配列、タプルは const を付けた配列、セットはハッシュテーブルに近いものです。この記事では、3つの違いと、要素の追加・削除のやり方、つまずきやすい点をまとめます。
3つの違いの一覧
| 種類 | 書き方 | 順番 | 同じ値の重複 | 作ったあとの変更 | 向いている使い方 |
|---|---|---|---|---|---|
| リスト(list) | [1, 2, 3] | ある | できる | できる | 順番のあるデータを追加・削除しながら使う |
| タプル(tuple) | (1, 2, 3) | ある | できる | できない | 座標や設定値など、変えない組 |
| セット(set) | {1, 2, 3} | ない | できない | できる | 重複をなくす、含まれているかをよく調べる |
セットには順番がないので、print したときの並び順も決まっていません。入れた順に表示されるとは限らないので、順番が大事なときはリストを使います。
カッコの書き方で間違えやすいところ
empty_list = [] # 空のリスト
empty_tuple = () # 空のタプル
empty_set = set() # 空のセット({} ではない)
empty_dict = {} # {} だけだと、セットではなく辞書になる
one = (1,) # 要素が1つのタプルは、最後にカンマが必要
not_tuple = (1) # カンマがないと、ただの数値の 1
print(type(empty_dict)) # <class 'dict'>
print(type(one)) # <class 'tuple'>
print(type(not_tuple)) # <class 'int'>タプルを作っているのは、実はカッコではなくカンマです。point = 10, 20 のようにカッコを省いても、タプルになります。
要素の追加と削除
リスト
fruits = ["apple", "banana"]
fruits.append("cherry") # 最後に1つ追加 → ['apple', 'banana', 'cherry']
fruits.insert(0, "melon") # 0番目に入れる → ['melon', 'apple', 'banana', 'cherry']
fruits.extend(["kiwi", "fig"]) # まとめて追加
fruits.remove("banana") # 最初に見つかった "banana" を削除(なければ ValueError)
last = fruits.pop() # 最後の要素を取り出して削除(pop(0) なら先頭)
fruits.clear() # すべて削除タプル
タプルは作ったあとに変更できないので、append や remove のようなメソッドはありません。要素を足したいときは、新しいタプルを作ります。
colors = ("red", "green")
# + でつなげて、新しいタプルを作る(元のタプルは変わらない)
more_colors = colors + ("blue",)
print(more_colors) # ('red', 'green', 'blue')
# 何度も変えるなら、リストにしてから操作し、最後にタプルに戻す
temp = list(colors)
temp.remove("green")
colors2 = tuple(temp)
print(colors2) # ('red',)何度も追加や削除をするデータなら、最初からリストにしておくほうが自然です。
セット
tags = {"python", "web"}
tags.add("server") # 1つ追加(すでにあれば何も起きない)
tags.update(["linux", "web"]) # まとめて追加("web" は重複するので増えない)
tags.discard("java") # 削除(なくてもエラーにならない)
tags.remove("linux") # 削除(なければ KeyError)
some = tags.pop() # どれか1つを取り出して削除(どれが出るかは決まっていない)セットには順番がないので、tags[0] のように番号で取り出すことはできません。
つまずきやすいところ
タプルの要素は書き換えられない(TypeError)
point = (1, 2, 3)
point[1] = 5 # TypeError: 'tuple' object does not support item assignmentただし、タプルの中にリストが入っている場合、そのリストの中身は変えられます。タプルが守っているのは「どのオブジェクトを指しているか」だけで、指している先の中身までは守りません。
record = ("taro", [80, 90])
record[1].append(70) # エラーにならない
print(record) # ('taro', [80, 90, 70])C言語でいえば、ポインタ自体が const でも、指している先のデータは書き換えられるのに似ています。
セットで重複を消すと、順番が崩れる
リストの重複を消すのに set を使うと手軽ですが、順番は残りません。順番を残したいときは dict.fromkeys を使います。
visits = ["tokyo", "osaka", "tokyo", "nagoya", "osaka"]
print(list(set(visits))) # 重複は消えるが、順番は決まっていない
print(list(dict.fromkeys(visits))) # ['tokyo', 'osaka', 'nagoya'](最初に出てきた順)セットにはリストを入れられない
セットの要素は、中身が変わらないもの(ハッシュ化できるもの)である必要があります。数値や文字列、タプルは入れられますが、リストや辞書、セットは入れられません。
ok = {1, "hello", (1, 2)} # 数値・文字列・タプルはOK
# ng = {[1, 2], [3, 4]} # TypeError: unhashable type: 'list'
# セットを入れたいときは、変更できない frozenset にする
groups = {frozenset({1, 2}), frozenset({3, 4})}上の record のように、中にリストを含むタプルも、セットには入れられません。
「含まれているか」を調べる速さ
x in データ で値が含まれているかを調べるとき、リストとタプルは先頭から順に探すので、要素が多いほど時間がかかります。セットはハッシュテーブルで管理されているので、要素が多くてもほぼ一定の時間で調べられます。大きなデータで何度も「含まれているか」を調べるなら、セットにしておくと速くなります。
allowed = {"admin", "editor", "viewer"}
print("editor" in allowed) # Trueセットの集合演算
セット同士は、記号で「両方にあるもの」「どちらかにあるもの」などを求められます。
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} どちらか一方だけにあるタプルの便利な使い方:分割代入
タプルは、関数から複数の値を返すときにも使われます。受け取る側では、それぞれの変数に分けて入れられます。
def min_max(numbers):
return min(numbers), max(numbers) # カンマで区切るとタプルで返る
low, high = min_max([3, 8, 1, 5])
print(low, high) # 1 8TypeScriptにも同じような「タプル」があり、「タプル(Tuple)とは?」で紹介しています。
まとめ:どれを使うか
- リスト
[]:順番が大事で、あとから追加や削除をするとき。迷ったらまずリスト - タプル
():座標や設定値など、変えない組。関数から複数の値を返すときや、辞書のキーにしたいとき - セット
{}:重複をなくしたいとき、「含まれているか」を何度も調べるとき、集合演算をしたいとき。空のセットはset()
値を表示するときの書き方は、「f文字列(f-string)の使い方」で紹介しています。



コメント