PythonのSet型に関する整理

2023.12.31

はじめに

PythonのSet型は、重複のない要素を格納するための非常に便利なデータ構造です。 例えば、リストから重複されたものを除いて、ユニークなリスト要素を取り出したい時などに便利です。

sample = [1, 1, 2, 3, 4]
print(set([1, 1, 2, 3])) # 出力: {1, 2, 3}

今回はSetの便利なメソッドを改めて整理していきます。

Setの生成方法

今回は以下の3つの例を紹介します。

# リストからの生成
set_from_list = set(['apple', 'banana', 'orange'])

# 波括弧を用いた生成
set_from_braces = {'apple', 'banana', 'orange'}

# Set内包表記を用いた生成
set_from_comprehension = {c for c in 'abracadabra' if c not in 'abc'}

波括弧を使うのはDict型も同じですが、キーを書かないとSetになります。

ただ、Setに入れることができる要素には制約があり、要素は全てHashableである必要があります。

Hashableについては以下の記事が参考になるかと思います。

例えばListはHashableではないのでエラーになります。

{[1, 2, 3], [4, 5, 6]}
# => TypeError: unhashable type: 'list'

Setの要素の追加・削除メソッド

以下ではまず初めにSetの要素の追加や削除などのメソッドを整理していきます。

add(elem)

Setに新しい要素を追加します。

sample = {1, 2, 3}
sample.add(4)
print(sample)  # 出力: {1, 2, 3, 4}

remove(elem)

Setから指定された要素を削除します。要素が存在しない場合はKeyErrorが発生します。

sample = {1, 2, 3}
sample.remove(2)
print(sample)  # 出力: {1, 3}
sample.remove(2) # KeyError: 2

discard(elem)

Setから指定された要素を削除します。要素が存在しなくてもエラーは発生しません。

sample = {1, 2, 3}
sample.discard(2)
print(sample)  # 出力: {1, 3}
sample.ddiscard(2) # エラーは発生しない

pop()

Setから要素を一つ削除して返します。Setが空の場合はKeyErrorが発生します。

sample = {1, 2, 3}
popped_element = sample.pop()
print(sample)  # 出力: {2, 3}
print(popped_element)  # 出力: 1
sample.pop()
sample.pop()
sample.pop() # 要素がないのでエラーが発生
# KeyError: 'pop from an empty set'

clear()

Set内のすべての要素を削除します。

sample = {1, 2, 3}
sample.clear()
print(sample)  # 出力: set()

要素に関する情報の取得

以下ではSetに含まれる要素に関しての操作を整理していきます。

len(s)

Set内の要素数(要素の個数)を取得します。

sample = {1, 2, 3, 4, 5}
print(len(sample))  # 出力: 5

x in s

要素xがSet内に存在するかどうかを確認します。

sample = {1, 2, 3, 4, 5}
print(3 in sample)  # 出力: True
print(6 in sample)  # 出力: False

2つのSetを使ったメソッド

Setの便利な点は他のSetとの共通部分を取り出したり、包含関係を調べたりといったSet同士の操作が可能な点もあるかと思います。 以下では2つのSetに関するメソッドを整理していきます。

isdisjoint(other)

Setが引数のSetと共通の要素を持っていないかどうかを確認します。

set1 = {1, 2, 3}
set2 = {4, 5, 6}
print(set1.isdisjoint(set2))  # 出力: True

set1 = {1, 2, 3}
set2 = {3, 4, 5}
# こっちは3が共通のためFalseになる
print(set1.isdisjoint(set2))  # 出力: False

issubset(other)

Setが引数のSetの部分集合かどうかを確認します。

set1 = {1, 2}
set2 = {1, 2, 3, 4}
print(set1.issubset(set2))  # 出力: True
print(set1 <= set2) # 出力: True

set1 = {1, 2, 3, 4}
set2 = {1, 2, 3, 4}
print(set1.issubset(set2))  # 出力: True
print(set1 <= set2) # 出力: True

set1 = {1, 2, 3, 4, 5}
set2 = {1, 2, 3, 4}
print(set1.issubset(set2))  # 出力: False
print(set1 <= set2) # 出力: False

set < other

Setが引数のSetの真部分集合(proper subset)かどうかを確認します。

set1 = {1, 2}
set2 = {1, 2, 3, 4}
print(set1 < set2)  # 出力: True

set1 = {1, 2, 3, 4}
set2 = {1, 2, 3, 4}
# 同じ要素を持つSetの場合はFalseになる
print(set1 < set2)  # 出力: False

issuperset(other)

Setが引数のSetの上位集合かどうかを確認します。 先ほどのsubsetの逆バージョンです。

set1 = {1, 2, 3, 4}
set2 = {1, 2}
print(set1.issuperset(set2))  # 出力: True

set > other

Setが引数のSetの真上位集合(proper superset)かどうかを確認します。

set1 = {1, 2, 3, 4}
set2 = {1, 2}
print(set1 > set2)  # 出力: True

union(*others)

Setと他のSetとの和集合を返します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1.union(set2))  # 出力: {1, 2, 3, 4, 5}
print(set1 | set2) # 出力: {1, 2, 3, 4, 5}

set1 = {1, 2, 3}
set2 = {3, 4, 5}
set3 = {5, 6, 7}
# 複数個まとめて渡すことも可能
print(set1.union(set2, set3))  # 出力: {1, 2, 3, 4, 5, 6, 7}

intersection(*others)

Setと他のSetとの積集合(共通部分)を返します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1.intersection(set2))  # 出力: {3}
print(set1 & set2) # 出力: {3}

difference(*others)

Setと他のSetとの差集合を返します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1.difference(set2))  # 出力: {1, 2}
print(set1 - set2)  # 出力: {1, 2}

print(set2.difference(set1))  # 出力: {4, 5}
print(set2 - set1)  # 出力: {4, 5}

symmetric_difference(other)

Setと他のSetとの対称差を返します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1.symmetric_difference(set2))  # 出力: {1, 2, 4, 5}
print(set1 ^ set2)  # 出力: {1, 2, 4, 5}

#以下と同じ。
print((set1 - set2) | (set2 - set1) ) # 出力: {1, 2, 4, 5}

Set型の更新メソッド

以下は副作用ありのメソッドです。 メソッドを使った方のSetの値が更新されてしまうので使う時は注意が必要です。

update(*others)

Setを他のSetと和集合に更新します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
set1.update(set2)
print(set1)  # 出力: {1, 2, 3, 4, 5}

intersection_update(*others)

Setを他のSetとの積集合に更新します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
set1.intersection_update(set2)
print(set1)  # 出力: {3}

difference_update(*others)

Setを他のSetとの差集合に更新します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
set1.difference_update(set2)
print(set1)  # 出力: {1, 2}

symmetric_difference_update(other)

Setを他のSetとの対称差に更新します。

set1 = {1, 2, 3}
set2 = {3, 4, 5}
set1.symmetric_difference_update(set2

)
print(set1)  # 出力: {1, 2, 4, 5}

最後に

個人的にはこういう痒い所に手が届くものが用意されているのはうれしいです。 ListやDict型に比べると使う機会は少ないかもしれませんが、存在だけ覚えておくとコードがシンプルに書けるシーンがいつかくると思います。

参考