frozensetとは?
Pythonのfrozensetは、標準のsetデータ型の不変(イミュータブル)バージョンです。通常のsetが変更可能なのに対し、frozensetは一度作成するとその内容を変更できません。つまり、要素の追加、削除、置換などの操作ができないのが特徴です。
この不変性によって、frozensetはハッシュ可能(hashable)になります。ハッシュ可能であることは、そのオブジェクトを辞書(dict)のキーとして使ったり、他のsetやfrozensetの要素として含めたりできることを意味します。
frozensetは重複のない要素の集まりであり、数学的な集合を実装するデータ型です。和集合(union)、積集合(intersection)、差集合(difference)などの標準的な集合演算をサポートし、イテラブル(繰り返し可能)なオブジェクトから作成できます。
【関連】
Pythonをもっと詳しく学ぶならpaizaラーニング
基本構文
Pythonでfrozensetを使うためには、frozensetコンストラクタを利用します。その際、引数として渡せるのは、イテラブルなオブジェクト(リスト、タプル、通常のsetなど)です。引数がない場合は、空のfrozensetが作成されます。
以下にfrozensetの基本的な作成方法と使い方を示します。
# リストからfrozensetを作成
ANIMALS = frozenset(["イヌ", "ネコ", "ウサギ"])
def main() -> None:
print(ANIMALS)
if __name__ == "__main__":
main()
出力結果(例)
frozenset({'イヌ', 'ウサギ', 'ネコ'})
frozensetはsetと同様に、要素の順序は保証されません。また、frozensetは重複した要素を持つことができないため、重複は自動的に除去されます。このページの以降のコードでも同様です。
# 重複要素を含むリストからfrozensetを作成
PETS = frozenset(["イヌ", "ネコ", "イヌ", "ハムスター", "ネコ"])
def main() -> None:
print(PETS)
if __name__ == "__main__":
main()
出力結果(例)
frozenset({'イヌ', 'ハムスター', 'ネコ'})
frozensetは一度作成すると変更できないため、addやremoveなどの変更メソッドはありませんが、他のfrozensetとの集合演算は可能です。
# 2つのfrozensetの演算
DOMESTIC_ANIMALS = frozenset(["イヌ", "ネコ", "ウマ"])
WILD_ANIMALS = frozenset(["ライオン", "トラ", "オオカミ"])
def main() -> None:
all_animals = DOMESTIC_ANIMALS.union(WILD_ANIMALS)
print(all_animals)
if __name__ == "__main__":
main()
出力結果(例)
frozenset({'イヌ', 'ウマ', 'オオカミ', 'トラ', 'ネコ', 'ライオン'})
実用例
frozensetはさまざまな状況で活用できる便利なデータ構造です。ここではいくつかの実用的な例を紹介します。
辞書のキーとしての使用
frozensetはハッシュ可能なので、辞書のキーとして使用できます。これは通常のsetではサポートされていない機能です。
# frozensetを辞書のキーとして使用するサンプル
MAMMALS = frozenset(["イヌ", "ネコ", "ウマ"])
BIRDS = frozenset(["ワシ", "スズメ", "カラス"])
ANIMAL_GROUPS = {
MAMMALS: "ホニュウルイ",
BIRDS: "トリ",
}
def main() -> None:
print(ANIMAL_GROUPS[MAMMALS])
if __name__ == "__main__":
main()
出力結果
ホニュウルイ
この例では、異なる動物のグループをfrozensetとして定義し、それらを辞書のキーとして使っています。この方法で、集合自体を識別子として使うことができます。
セット演算の活用
frozensetは通常のsetと同じく、和集合、積集合、差集合などの集合演算をサポートしています。
# セット演算の例
PETS = frozenset(["イヌ", "ネコ", "ハムスター"])
FARM_ANIMALS = frozenset(["ウシ", "ウマ", "ヒツジ", "イヌ"])
def main() -> None:
# 和集合(union)
all_domestic = PETS.union(FARM_ANIMALS)
print("すべての家畜:", all_domestic)
if __name__ == "__main__":
main()
出力結果(例)
すべての家畜: frozenset({'イヌ', 'ウシ', 'ウマ', 'ハムスター', 'ヒツジ', 'ネコ'})
この例では、ペットと家畜の集合の和集合を求めています。両方の集合にある動物がすべて含まれた新しいfrozensetが作成されます。
不変性を利用したデータ保護
frozensetの不変性は、データが変更されないことを保証したい場合に役立ちます。
# 不変性を利用したデータ保護
PROTECTED_SPECIES = frozenset(["ジャイアントパンダ", "アムールトラ", "イリオモテヤマネコ"])
def check_animal_status(animal: str) -> str:
if animal in PROTECTED_SPECIES:
return f"{animal}は保護対象です"
return f"{animal}は保護対象ではありません"
def main() -> None:
print(check_animal_status("イリオモテヤマネコ"))
if __name__ == "__main__":
main()
出力結果
イリオモテヤマネコは保護対象です
ここでは、保護対象の動物リストをfrozensetとして定義しています。この方法により、リストが誤って変更されることを防ぎ、データの整合性を保証しています。
タプルとの組み合わせ
frozensetはタプルと組み合わせることで、複雑なデータ構造を作成できます。
# タプルとfrozensetの組み合わせ
ANIMAL_GROUPS = {
("ホニュウルイ", frozenset(["イヌ", "ネコ", "ウマ"])): "陸上に生息",
("サカナ", frozenset(["マグロ", "サケ", "タイ"])): "水中に生息",
}
def main() -> None:
for (group_name, animals), habitat in ANIMAL_GROUPS.items():
animal_list = ", ".join(animals)
print(f"{group_name}({animal_list})は{habitat}")
if __name__ == "__main__":
main()
出力結果(例)
ホニュウルイ(イヌ, ネコ, ウマ)は陸上に生息
サカナ(マグロ, タイ, サケ)は水中に生息
この例では、動物のグループ名とそのメンバーをタプルとfrozensetの組み合わせで表現し、辞書のキーとして使用しています。
高速な検索と比較
frozensetは内部的にハッシュテーブルを使用しているため、要素の検索が非常に高速です。
# 高速な検索
LARGE_ANIMALS = frozenset(["ゾウ", "キリン", "サイ", "クジラ", "カバ"])
def is_large_animal(animal: str) -> bool:
# frozenset による検索は O(1) の時間複雑度
return animal in LARGE_ANIMALS
def main() -> None:
print(is_large_animal("ゾウ"))
print(is_large_animal("ネズミ"))
if __name__ == "__main__":
main()
出力結果
True
False
この例では、大型動物のリストをfrozensetとして保持し、特定の動物が大型かどうかを高速に判定しています。
frozensetと通常のsetの変換
frozensetと通常のsetは相互に変換できます。変更が必要な操作を行った後、再びfrozensetに変換するといったパターンが可能です。
# frozensetとsetの相互変換
ORIGINAL = frozenset(["ライオン", "トラ", "ヒョウ"])
def main() -> None:
# frozenset -> set(変更可能に)
mutable = set(ORIGINAL)
mutable.add("チーター")
# set -> frozenset(再び不変に)
updated = frozenset(mutable)
print(updated)
if __name__ == "__main__":
main()
出力結果(例)
frozenset({'チーター', 'トラ', 'ヒョウ', 'ライオン'})
この例では、まずfrozensetを通常のsetに変換して要素を追加し、その後再びfrozensetに戻しています。
複合データ構造での活用
frozensetは他の複合データ構造と組み合わせることで、より柔軟なデータモデリングが可能になります。
# 複合データ構造の例
ANIMAL_DATA = {
"哺乳類": frozenset(["イヌ", "ネコ", "ウマ"]),
"鳥類": frozenset(["ワシ", "スズメ", "カラス"]),
"爬虫類": frozenset(["ヘビ", "トカゲ", "カメ"]),
}
def main() -> None:
for category, animals in ANIMAL_DATA.items():
animal_list = ", ".join(animals)
print(f"{category}の例: {animal_list}")
if __name__ == "__main__":
main()
出力結果(例)
哺乳類の例: イヌ, ネコ, ウマ
鳥類の例: ワシ, スズメ, カラス
爬虫類の例: ヘビ, トカゲ, カメ
この例では、動物のカテゴリごとに異なるfrozensetを持つ辞書を作成しています。このような構造により、データの整理と参照が容易になります。
関数引数としての利用
frozensetは関数の引数として使用することで、不変なデフォルト値を提供できます。
# 関数引数としてのfrozenset
DEFAULT_CATEGORIES = frozenset(["ホニュウルイ", "チョウルイ"])
def analyze_animals(
animals: list[dict[str, str]],
categories: frozenset = DEFAULT_CATEGORIES,
) -> int:
valid_animals = [a for a in animals if a["category"] in categories]
return len(valid_animals)
def main() -> None:
test_animals = [
{"name": "イヌ", "category": "ホニュウルイ"},
{"name": "ワシ", "category": "チョウルイ"},
{"name": "ヘビ", "category": "ハチュウルイ"},
]
print(analyze_animals(test_animals))
if __name__ == "__main__":
main()
出力結果
2
この例では、関数のデフォルト引数としてfrozensetを使用しています。通常のミュータブルなオブジェクト(リストなど)をデフォルト引数として使うと予期せぬ動作を引き起こす可能性がありますが、frozensetなら安全です。
まとめ
Pythonのfrozensetは、不変な集合型データ構造として、データの整合性が重要なシチュエーションで非常に役立ちます。通常のsetの機能を維持しながら、変更不可能という特性を持つことで、特に辞書のキーやsetの要素として使用できるのが大きな利点です。
frozensetの活躍する場面
- データの不変性を保証したい場合
- 辞書のキーとして集合を使いたい時
- 集合演算を活用したデータ処理が必要な場面
重要なポイント
- 一度作成すると内容を変更できない
- ハッシュ可能なため辞書のキーとして使える
- 通常のsetとほぼ同じ集合演算が可能
Pythonプログラミングにおいて、データの変更を防ぎたい場合や、集合をキーとして使いたい場合には、frozensetを活用することで、より安全で効率的なコードを書くことができます。特に、複雑なデータ構造やキャッシュ機構、関数のデフォルト引数などのシナリオでは、frozensetの特性が真価を発揮するでしょう。
frozensetは一見すると地味な存在ですが、適切に活用すれば、コードの安全性と可読性を高める強力な武器になります。集合論的アプローチとPythonの機能を組み合わせて、データ処理の可能性を広げてみてください。
レベルを更に上げたい方はpaizaプログラミングスキルチェックへ