「リストの中から重複をなくしたい」
「AとBに共通する要素だけを取り出したい」
そんな疑問を解決するために、今回はset(セット・集合)について、初心者の方でも理解できるように詳しく説明していきます。
setとは?「重複を許さない集合」のこと

setは、数学の集合と同じように、要素の重複を許さず、順番を持たないデータ構造です。
setの特徴
- 重複なし:同じ要素は1つしか持てない
- 順序なし:要素の順番は保証されない
- 変更可能:要素の追加・削除ができる
- 高速検索:要素の存在確認が非常に速い
基本的な作り方
# 波括弧{}を使って作成
fruits = {"りんご", "バナナ", "みかん"}
print(fruits) # {'みかん', 'バナナ', 'りんご'} ※順番は不定
# set()関数を使って作成
numbers = set([1, 2, 3, 4, 5])
print(numbers) # {1, 2, 3, 4, 5}
# 空のsetを作成
empty_set = set() # 注意:{}は辞書になる
print(empty_set) # set()
リストとsetの変換:重複を簡単に除去

リストからsetへ(重複除去)
# 重複のあるリスト
numbers = [1, 2, 2, 3, 3, 3, 4, 5]
print("元のリスト:", numbers) # [1, 2, 2, 3, 3, 3, 4, 5]
# setに変換(重複が自動で除去される)
unique_numbers = set(numbers)
print("setに変換:", unique_numbers) # {1, 2, 3, 4, 5}
# 実用的な例:名前の重複を除去
names = ["太郎", "花子", "太郎", "次郎", "花子", "美香"]
unique_names = set(names)
print("重複除去後:", unique_names) # {'次郎', '美香', '太郎', '花子'}
setからリストへ(順序が必要な場合)
fruits_set = {"りんご", "バナナ", "みかん"}
# リストに戻す
fruits_list = list(fruits_set)
print(fruits_list) # ['みかん', 'バナナ', 'りんご'] ※順番は不定
# ソートしてから戻す
sorted_fruits = sorted(fruits_set)
print(sorted_fruits) # ['みかん', 'りんご', 'バナナ'] ※あいうえお順
setに要素を追加・削除する方法
要素を追加する
fruits = {"りんご", "バナナ"}
print("最初:", fruits) # {'バナナ', 'りんご'}
# 1つずつ追加
fruits.add("みかん")
print("追加後:", fruits) # {'バナナ', 'みかん', 'りんご'}
# 同じ要素を追加しても重複しない
fruits.add("りんご") # 既に存在する
print("重複追加後:", fruits) # {'バナナ', 'みかん', 'りんご'} ※変化なし
複数の要素を一度に追加する
fruits = {"りんご", "バナナ"}
# update()でリストから複数追加
fruits.update(["ぶどう", "もも", "すいか"])
print(fruits) # {'すいか', 'もも', 'ぶどう', 'バナナ', 'りんご'}
# 文字列からも追加できる(1文字ずつ追加される)
letters = set()
letters.update("python")
print(letters) # {'y', 'h', 'n', 'p', 't', 'o'}
要素を削除する
fruits = {"りんご", "バナナ", "みかん", "ぶどう"}
# remove():存在しない場合はエラー
fruits.remove("バナナ")
print(fruits) # {'ぶどう', 'みかん', 'りんご'}
# discard():存在しなくてもエラーにならない
fruits.discard("なし") # 存在しないがエラーなし
print(fruits) # {'ぶどう', 'みかん', 'りんご'}
# pop():ランダムに1つ削除して返す
removed = fruits.pop()
print(f"削除された要素: {removed}")
print(f"残りの要素: {fruits}")
# clear():すべての要素を削除
fruits.clear()
print(fruits) # set()
集合演算:setの最も強力な機能
setの最大の特徴は、数学の集合演算が簡単にできることです。
和集合(union):2つのsetを合体
group_a = {"太郎", "花子", "次郎"}
group_b = {"花子", "美香", "健太"}
# 方法1:|演算子を使用
all_members = group_a | group_b
print("全メンバー:", all_members) # {'健太', '太郎', '次郎', '美香', '花子'}
# 方法2:union()メソッドを使用
all_members2 = group_a.union(group_b)
print("全メンバー:", all_members2) # 同じ結果
積集合(intersection):共通する要素だけ
course_a = {"太郎", "花子", "次郎", "美香"}
course_b = {"花子", "次郎", "健太", "さくら"}
# 方法1:&演算子を使用
both_courses = course_a & course_b
print("両方受講:", both_courses) # {'次郎', '花子'}
# 方法2:intersection()メソッドを使用
both_courses2 = course_a.intersection(course_b)
print("両方受講:", both_courses2) # 同じ結果
差集合(difference):片方にだけある要素
all_students = {"太郎", "花子", "次郎", "美香", "健太"}
absent_students = {"次郎", "健太"}
# 方法1:-演算子を使用
present_students = all_students - absent_students
print("出席者:", present_students) # {'太郎', '美香', '花子'}
# 方法2:difference()メソッドを使用
present_students2 = all_students.difference(absent_students)
print("出席者:", present_students2) # 同じ結果
対称差集合(symmetric difference):どちらか一方にだけある要素
team_a = {"太郎", "花子", "次郎"}
team_b = {"次郎", "美香", "健太"}
# 方法1:^演算子を使用
exclusive_members = team_a ^ team_b
print("どちらか一方のメンバー:", exclusive_members) # {'太郎', '健太', '美香', '花子'}
# 方法2:symmetric_difference()メソッドを使用
exclusive_members2 = team_a.symmetric_difference(team_b)
print("どちらか一方のメンバー:", exclusive_members2) # 同じ結果
実践的な使用例

重複データの除去と分析
# アンケートの回答データ
responses = [
"Python", "Java", "Python", "C++", "JavaScript",
"Python", "Ruby", "Java", "Go", "Python"
]
# 重複を除去して回答の種類を確認
unique_responses = set(responses)
print(f"回答の種類: {len(unique_responses)}種類")
print(f"内容: {sorted(unique_responses)}")
# 各言語の人気度を計算
for language in unique_responses:
count = responses.count(language)
print(f"{language}: {count}票")
ユーザーの権限管理
# ユーザーの権限管理
admin_permissions = {"read", "write", "delete", "admin"}
editor_permissions = {"read", "write"}
viewer_permissions = {"read"}
def check_permissions(user_type, required_permission):
"""ユーザーが特定の権限を持っているかチェック"""
if user_type == "admin":
permissions = admin_permissions
elif user_type == "editor":
permissions = editor_permissions
else:
permissions = viewer_permissions
return required_permission in permissions
# 使用例
print(check_permissions("editor", "write")) # True
print(check_permissions("viewer", "delete")) # False
# 追加の権限が必要な操作を確認
def get_missing_permissions(user_permissions, required_permissions):
"""不足している権限を返す"""
return required_permissions - user_permissions
user_perms = {"read", "write"}
required_perms = {"read", "write", "delete", "admin"}
missing = get_missing_permissions(user_perms, required_perms)
print(f"不足している権限: {missing}") # {'admin', 'delete'}
データベースの更新処理
# データベースの更新処理の例
existing_users = {"user1", "user2", "user3", "user4"}
new_user_list = {"user2", "user3", "user5", "user6"}
# 新規追加するユーザー
users_to_add = new_user_list - existing_users
print(f"追加するユーザー: {users_to_add}") # {'user6', 'user5'}
# 削除するユーザー
users_to_remove = existing_users - new_user_list
print(f"削除するユーザー: {users_to_remove}") # {'user1', 'user4'}
# 変更がないユーザー
unchanged_users = existing_users & new_user_list
print(f"変更なし: {unchanged_users}") # {'user2', 'user3'}
文字列の文字種別分析
def analyze_text(text):
"""テキストの文字種別を分析する"""
text_set = set(text.lower())
vowels = set("aiueo")
consonants = set("bcdfghjklmnpqrstvwxyz")
digits = set("0123456789")
found_vowels = text_set & vowels
found_consonants = text_set & consonants
found_digits = text_set & digits
print(f"母音: {sorted(found_vowels)}")
print(f"子音: {sorted(found_consonants)}")
print(f"数字: {sorted(found_digits)}")
# 特殊文字
special_chars = text_set - vowels - consonants - digits - {' '}
print(f"特殊文字: {sorted(special_chars)}")
# 使用例
analyze_text("Hello Python 2024!")
setの制限と注意点

ハッシュ可能な要素のみ追加可能
# 間違い:リストや辞書は追加できない
s = set()
# s.add([1, 2, 3]) # TypeError: unhashable type: 'list'
# s.add({"a": 1}) # TypeError: unhashable type: 'dict'
# 正しい:不変な型は追加可能
s.add(1) # 数値
s.add("text") # 文字列
s.add((1, 2, 3)) # タプル
print(s) # {1, 'text', (1, 2, 3)}
順序は保証されない
# setは順序を保持しない
numbers = {3, 1, 4, 1, 5, 9, 2, 6}
print(numbers) # 順序は不定
# 順序が必要な場合はリストに変換
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 2, 3, 4, 5, 6, 9]
空のsetの作り方に注意
# 間違い:これは辞書になる
empty_dict = {}
print(type(empty_dict)) # <class 'dict'>
# 正しい:空のsetを作る
empty_set = set()
print(type(empty_set)) # <class 'set'>
setのパフォーマンス
setは要素の検索が非常に高速です。
import time
# 大量のデータを準備
large_list = list(range(100000))
large_set = set(range(100000))
# リストでの検索時間
start = time.time()
result = 99999 in large_list
list_time = time.time() - start
# setでの検索時間
start = time.time()
result = 99999 in large_set
set_time = time.time() - start
print(f"リストでの検索時間: {list_time:.6f}秒")
print(f"setでの検索時間: {set_time:.6f}秒")
print(f"setは約{list_time/set_time:.0f}倍高速")
よくある質問

Q:同じ要素を何度add()しても大丈夫?
A:はい、問題ありません。setでは重複は自動的に排除されます。
s = set()
s.add("apple")
s.add("apple") # 重複
s.add("apple") # 重複
print(s) # {'apple'} ※1つだけ保持
Q:順番を保持したいsetのような機能はない?
A:Python 3.7以降では、辞書が挿入順序を保持するようになったので、辞書のキーを使って順序付きsetのような使い方ができます。
# 順序を保持する「setのような」使い方
ordered_set = {}
for item in ["c", "a", "b", "a", "c"]:
ordered_set[item] = None
print(list(ordered_set.keys())) # ['c', 'a', 'b'] ※挿入順序を保持
Q:frozensetとの違いは?
A:frozensetは不変(immutable)なsetです。作成後に要素の追加・削除ができません。
# 通常のset(変更可能)
normal_set = {1, 2, 3}
normal_set.add(4) # OK
# frozenset(変更不可)
frozen_set = frozenset([1, 2, 3])
# frozen_set.add(4) # AttributeError
Q:setの要素をfor文で取り出す順序は?
A:順序は保証されません。毎回異なる順序になる可能性があります。
s = {1, 2, 3, 4, 5}
for item in s:
print(item) # 順序は不定
まとめ
pythonのsetは、重複のないデータ管理と高速検索に最適なデータ構造です。
おさらい:
- 重複排除:リストからsetに変換するだけで重複を除去
- 集合演算:和集合(|)、積集合(&)、差集合(-)、対称差集合(^)
- 高速検索:in演算子での検索がリストより圧倒的に高速
- 制限事項:順序なし、ハッシュ可能な要素のみ
コメント