タプルとセット
リストはすでに知っていますね。Pythonにはリストでは解決できない問題に対応する、もう2つのコレクション型があります。タプルは決して変更されない固定の値のグループを保持します。セットは一意の値のみを保持し、コレクションがどれだけ大きくなってもメンバーシップを即座にチェックできます。
タプル
タプルは、作成後に変更できない順序付けされた値のグループです。括弧でタプルを定義しますが、これは省略可能です。実際にタプルにするのはカンマです。1要素のタプルには末尾にカンマが必要です。
point = (10, 20)
rgb = (255, 128, 0)
dimensions = (1920, 1080)
single = (42,) # 1要素のタプルには末尾のカンマが必要
also_tuple = 42, 99 # 括弧は省略可能;カンマがタプルを作るインデックスによるアクセスはリストとまったく同じように動作します。要素を変更しようとするとTypeErrorが発生します:
point = (10, 20)
point[0] # 10
point[1] # 20
point[-1] # 20
point[0] = 99 # TypeError: 'tuple' object does not support item assignmentタプルを使うとき
タプルは、互いに関連していて変更されない小さな値のグループに対して使用します。座標(x, y)、色(r, g, b)、名前とスコアのペア("さくら", 87)などです。固定された構造は、コードを読む人にこのグループが単一の単位として扱われることを示します。
locations = {}
locations[(40, -74)] = "東京" # タプルを辞書のキーとして使用、動作する
locations[[40, -74]] = "東京" # リストを辞書のキーとして使用、TypeErrorアンパック
アンパックはタプルから値を取り出し、それぞれを1行で独自の名前に代入します。名前の数は値の数と一致する必要があります。残りの項目をリストとして取得するには*を使用します。
point = (10, 20)
x, y = point
print(x) # 10
print(y) # 20
first, *rest = [1, 2, 3, 4, 5]
# first = 1, rest = [2, 3, 4, 5]
head, *middle, tail = [1, 2, 3, 4, 5]
# head = 1, middle = [2, 3, 4], tail = 5名前付きタプル
名前付きタプルは、各位置に名前があるタプルです。point[0]がx座標であることを覚える代わりに、point.xと書きます。値は依然としてイミュータブルです;数値の位置の代わりに、読みやすい属性名が得られるだけです。
名前付きタプルのインポート
namedtupleはPythonの標準ライブラリにありますが、インポートする必要があります。from collections import namedtupleの行はこのコースで最初のインポートです。インポートについてはモジュールの章で完全に扱われます。
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
Player = namedtuple("Player", ["name", "score", "level"])
p = Point(10, 20)
p.x # 10
p.y # 20
sakura = Player("さくら", 87, 5)
sakura.name # "さくら"
sakura.score # 87セット
セットは順序が保証されない一意の値のコレクションです。同じ値を2回追加しても何も起こりません:セットは各項目のコピーを1つだけ保持します。要素を持つセットには波括弧を使用し、空のセットを作成するにはset()を使用します。
tags = {"python", "beginner", "tutorial"}
numbers = {1, 2, 3, 4, 5}
empty = set() # {} ではない(それは空の辞書)同じ値を2回追加してもセットは変更されません:
tags.add("python") # tagsは変更されない、"python"はすでに含まれているセットを使うとき
セットは3つのことに最適なツールです:リストから重複を削除すること、大きなコレクションに何かが含まれているかを素早くチェックすること、そして2つのグループを比較して共有しているものや異なるものを見つけることです。
# リストから重複を削除
raw = ["cat", "dog", "cat", "bird", "dog", "cat"]
unique = list(set(raw)) # ["cat", "dog", "bird"](順序は保証されない)# 高速なメンバーシップチェック
valid_codes = {"USD", "EUR", "GBP", "JPY"}
code = "EUR"
if code in valid_codes: # 何千ものコードがあっても即座に検索
print("Valid")セット操作
セットは数学で学んだのと同じ操作をサポートします:和集合(どちらかのセットにあるすべて)、積集合(両方のセットが共有するもののみ)、差集合(一方が持っていてもう一方が持っていないもの)。Pythonはこれらに演算子記号を使い、それぞれにメソッドの同等物があります。
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # {1, 2, 3, 4, 5, 6} (和集合:どちらかにあるすべて)
a & b # {3, 4} (積集合:両方にあるもののみ)
a - b # {1, 2} (差集合:aにあるがbにない)
b - a # {5, 6} (逆方向の差集合)
a ^ b # {1, 2, 5, 6} (対称差:一方にあるが両方にはない)これらにはメソッド形式もあります:.union()、.intersection()、.difference()、.symmetric_difference()。
セットの変更
セットはミュータブルです。.add()は1つの項目を追加します。.update()は任意のリストやその他のイテラブルから複数の項目を一度に追加します。.remove()は項目を削除しますが、そこにない場合はエラーを発生させます。.discard()は項目が存在すれば静かに削除し、存在しなければ何もしません。
tags = {"python", "beginner"}
tags.add("tutorial") # 1つの項目を追加
tags.update(["web", "api"]) # 任意のイテラブルから複数の項目を追加
tags.remove("beginner") # 削除、見つからなければKeyError
tags.discard("missing") # 削除、見つからなくてもエラーなし
tags.pop() # 任意の項目を削除して返す
tags.clear() # すべてを削除項目が存在するかわからない場合は.discard()を使用してください。
凍結セット
凍結セット(frozen set)は、作成後に変更できないセットです。これを使う主な理由:凍結セットはハッシュ可能なので、辞書のキーとして使ったり、他のセットの中に格納したりできます。
valid_statuses = frozenset({"active", "paused", "deleted"})
valid_statuses.add("archived") # AttributeError、frozensetはイミュータブル適切なコレクションの選択
4つの型、それぞれに明確な役割があります。データで何をする必要があるかを尋ねれば、適切な選択は通常明らかになります。
| list | tuple | set | dict | |
|---|---|---|---|---|
| 順序付け | あり | あり | なし | あり(挿入順) |
| ミュータブル | はい | いいえ | はい | はい |
| 重複 | あり | あり | なし | なし(キー) |
| アクセス方法 | インデックス | インデックス | 該当なし | キー |
| 使う場面 | 順序付けされた変更可能なシーケンス | 固定レコード | 一意の値、高速なメンバーシップ | キー・バリュー検索 |
簡単な決定ルール:
- 名前で何かを検索する必要がある? → dict
- 変更する順序付けされたコレクションが必要? → list
- 関連する値の固定されたグループがある? → tuple
- 一意の値や高速なメンバーシップテストが必要? → set
実践
固定レコードを格納するためのタプルと、一意の値を追跡するためのセットの使用:
home = (35.6762, 139.6503) # 緯度、経度
office = (35.6895, 139.6917)
home_lat, home_lon = home
print(f"Home: {home_lat}, {home_lon}")
# セットでユニークな訪問者を追跡
visitors = set()
visitors.add("さくら")
visitors.add("ゆうき")
visitors.add("さくら") # すでにセットに含まれている、静かに無視される
visitors.add("ひかり")
print(f"Unique visitors: {len(visitors)}")
print(f"さくら visited: {'さくら' in visitors}")
print(f"だいち visited: {'だいち' in visitors}")
