閉じる

Pythonのsetから和集合や積集合を作る方法(unionとintersection)

Pythonのsetは、重複排除や要素の高速な所属判定に優れたデータ構造です。

本記事では、和集合(union)と積集合(intersection)を中心に、演算子とメソッドの違い、空集合の作り方、型の注意点、frozensetの活用までを丁寧に解説します。

読みやすさと実行結果の安定性を意識しながら、実用的なサンプルで理解を深めます。

setで和集合・積集合を扱う基礎

setの特徴(重複なし・順序なし)

setは要素の重複を許さず、順序を保持しません

そのため、同じ値を複数回追加しても1つにまとまり、表示順序は実行ごとに異なる場合があります。

メンバーシップ判定(要素が含まれるか)が高速で、集合演算に向いています。

Python
# 重複が自動的に取り除かれることを確認する
s = {1, 2, 2, 3, 3, 3}
print(s)  # 表示順序は保証されない
print(2 in s)  # 所属判定は高速
print(len(s))  # ユニーク要素数

# 表示順を安定させたい場合は sorted で並べ替える
print(sorted(s))
実行結果
{1, 2, 3}
True
3
[1, 2, 3]

補足

setはミュータブル(変更可能)です。

後述のupdateintersection_updateは集合をその場で更新します。

空集合の作り方(set)と要素(ハッシュ可能)

{}は辞書(dict)を表すため、空集合は必ずset()で作ります

また、setの要素はハッシュ可能(不変)である必要があります

例えばintstrtuplefrozensetはOKですが、listdictはNGです。

Python
# 空集合の正しい作り方
empty_set = set()
print(type(empty_set))

# {} は空の辞書になる
empty_braces = {}
print(type(empty_braces))

# ハッシュ可能な要素はOK
ok = {1, "a", (10, 20)}  # tupleは不変なのでOK
print(ok)

# ハッシュ不可能な要素はエラー
try:
    ng = {1, [2, 3]}  # listは不変ではないためNG
except TypeError as e:
    print("TypeError:", e)
実行結果
<class 'set'>
<class 'dict'>
{1, 'a', (10, 20)}
TypeError: unhashable type: 'list'

和集合の作り方(union, |)

unionの基本構文と戻り値(非破壊)

和集合は「いずれかの集合に含まれる要素の全体」です

set.unionは新しい集合を返し、元の集合は変更しません。

任意のイテラブルを受け取れるのも特徴です。

Python
# union は新しい set を返す(非破壊)
a = {1, 2}
b = {2, 3}
u = a.union(b)        # a, b は変化しない
print("u:", sorted(u))
print("a:", sorted(a), "b:", sorted(b))

# イテラブル(例: list)も渡せる
u2 = a.union([3, 4, 4])
print("u2:", sorted(u2))
実行結果
u: [1, 2, 3]
a: [1, 2] b: [2, 3]
u2: [1, 2, 3, 4]

|演算子の使い方と可読性

|演算子でも和集合を作れます。

短く書けて読みやすい一方で、右側はset系型である必要があります

リストなどと組み合わせる場合はunionを使います。

Python
a = {1, 2}
b = {2, 3}

# 演算子での和集合
print(sorted(a | b))

# 右側が set でないと TypeError
try:
    print(a | [3, 4])  # list は不可
except TypeError as e:
    print("TypeError:", e)
実行結果
[1, 2, 3]
TypeError: unsupported operand type(s) for |: 'set' and 'list'

複数集合の和集合

複数の集合をまとめたい場合、unionは可変長引数に対応しています。

演算子は連結できますが、読みやすさの観点ではメソッドで一気に渡す方法が明瞭です。

Python
a = {1, 2}
b = {2, 3}
c = {3, 4}

# メソッドでまとめて
u1 = a.union(b, c)
print("union:", sorted(u1))

# 演算子を連結
u2 = a | b | c
print("op:", sorted(u2))
実行結果
union: [1, 2, 3, 4]
op: [1, 2, 3, 4]

update(|=)でその場で和集合

集合をその場で更新したい場合はupdate|=を使います

メモリ効率が良く、大きな集合で有利です。

updateは任意のイテラブルを受け付けます。

Python
a = {1, 2}
b = {2, 3}

# メソッドでのインプレース更新
a.update(b)         # a が更新される
print("a after update:", sorted(a))

# 演算子でのインプレース更新
a |= {4, 5}         # さらに a を更新
print("a after |= :", sorted(a))
実行結果
a after update: [1, 2, 3]
a after |= : [1, 2, 3, 4, 5]

積集合の作り方(intersection, &)

intersectionの基本構文と戻り値(非破壊)

積集合は「全ての集合に共通する要素」です

set.intersectionは新しい集合を返し、任意のイテラブルを受け取れます。

Python
a = {1, 2, 3}
b = {2, 3, 4}

i = a.intersection(b)    # a, b は変化しない
print("i:", sorted(i))

# イテラブル(例: list)も渡せる
i2 = a.intersection([2, 99])
print("i2:", sorted(i2))
実行結果
i: [2, 3]
i2: [2]

&演算子の使い方と注意点

&演算子は積集合を簡潔に書けますが、右側はset系型である必要があります

リストなどとの積集合はintersectionを用います。

Python
a = {1, 2, 3}
b = {2, 3, 4}

print(sorted(a & b))  # OK

try:
    print(a & [2, 3])  # list は不可
except TypeError as e:
    print("TypeError:", e)
実行結果
[2, 3]
TypeError: unsupported operand type(s) for &: 'set' and 'list'

複数集合の積集合

複数集合の積集合もintersectionでまとめて指定できます。

演算子は連結も可能です。

Python
a = {1, 2, 3, 4}
b = {2, 3, 5}
c = {0, 2, 3, 9}

# メソッドでまとめて
i1 = a.intersection(b, c)
print("intersection:", sorted(i1))

# 演算子で連結
i2 = a & b & c
print("op:", sorted(i2))
実行結果
intersection: [2, 3]
op: [2, 3]

intersection_update(&=)でその場で積集合

その場で更新する場合はintersection_update&=を使います。

大規模データで特に有効です。

Python
a = {1, 2, 3, 4}
b = {2, 3, 5}

a.intersection_update(b)  # a が共通要素のみになる
print("a after intersection_update:", sorted(a))

a &= {2}  # さらに a を絞り込む
print("a after &= :", sorted(a))
実行結果
a after intersection_update: [2, 3]
a after &= : [2]

つまずきやすい注意点とベストプラクティス

並び順は保証されない(表示順に注意)

setは順序を保持しないため、printの表示順は保証されません

テストやログで安定した表示が必要な場合はsortedで並べ替えて出力しましょう。

Python
s = {"b", "a", "c"}
print("raw:", s)              # 順序は不定
print("sorted:", sorted(s))   # 安定した順序
実行結果
raw: {'b', 'a', 'c'}
sorted: ['a', 'b', 'c']

空集合との演算結果(和集合/積集合)

空集合との演算は覚えておくと便利です。

和集合は元の集合のまま積集合は常に空集合になります。

Python
s = {1, 2, 3}
empty = set()

print("union with empty:", sorted(s.union(empty)))
print("intersection with empty:", s.intersection(empty))  # 空集合
実行結果
union with empty: [1, 2, 3]
set()

型の混在と比較の注意

Pythonでは同値とみなされる異種型があります。

例えば1 == True1 == 1.0Trueのため、setでは重複として扱われ1つにまとまります

意図しない重複除去を避けるため、型を統一しましょう。

Python
s = {1, True, 1.0, 0, False}
print(s)            # 1 と True、0 と False は同一とみなされる
print(len(s))       # 要素数に注意

# NaN は自分自身と等しくないため重複しない例
import math
t = {float('nan'), float('nan')}
print(t, len(t))    # 2 つ入ることがある(実装依存のハッシュだが == が False のため区別される)
実行結果
{0, 1.0}
2
{nan, nan} 2

frozensetの使いどころ(辞書キー)

frozensetは不変の集合で、辞書のキーやsetの要素にできます。

ネストした集合を扱う場合に便利です。

Python
# frozenset を set の要素にする(ネスト)
inner1 = frozenset({1, 2})
inner2 = frozenset({2, 3})
outer = {inner1, inner2}
print(outer)

# frozenset を辞書キーにする
paths = {
    frozenset({"A", "B"}): 10,
    frozenset({"B", "C"}): 20,
}
print(paths[frozenset({"B", "A"})])  # 順序に依存しないキー
実行結果
{frozenset({1, 2}), frozenset({2, 3})}
10

演算子かメソッドか(読みやすさと速度)

基本方針として、相手がset系なら演算子、イテラブルをそのまま渡したいならメソッドが読みやすいです。

パフォーマンス差は通常ごく小さく、インプレース更新(update系)はメモリ効率に優れます

下表は違いの要点です。

操作非破壊演算子非破壊メソッドその場更新相手が任意イテラブル
和集合union= / updateメソッドは可、演算子は不可
積集合&intersection&= / intersection_updateメソッドは可、演算子は不可
Python
a = {1, 2}
# 相手が list のときはメソッドで
u = a.union([2, 3, 4])  # OK
print(sorted(u))

# 大きな集合を効率よく更新したいときは in-place
a.update({5, 6})  # 余計なコピーを作らない
print(sorted(a))
実行結果
[1, 2, 3, 4]
[1, 2, 5, 6]

まとめ

本記事では、setでの和集合と積集合の基本から、演算子|&、メソッドunionintersection、さらにupdate系のインプレース更新までを体系的に解説しました。

相手がset系なら演算子、任意イテラブルならメソッドという指針を持つと、読みやすく安全なコードになります。

表示順は保証されない点、空集合との演算、型の同値性(1とTrueなど)やfrozensetの活用も押さえておくと、実務でのバグを避けやすくなります。

実装ではテスト出力にsortedを使って安定化し、大規模集合ではインプレース更新でメモリ効率を確保すると良いでしょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!