8.7. sets — 重複のない要素の順序なしコレクション

バージョン 2.3 で追加.

バージョン 2.6 で撤廃: 組み込みの set/frozenset 型がこのモジュールを置き換えます。

sets モジュールは、ユニークな要素の順序なしコレクションを構築し、操作するためのクラスを提供します。帰属関係のテストやシーケンスから重複を取り除いたり、積集合・和集合・差集合・対称差集合のような標準的な数学操作などを含みます。

集合は、他のコレクションと同様、 x in set, len(set), for x in set をサポートします。コレクションには順序がないので、集合は挿入の順序や要素の位置を記録しません。従って、集合はインデクシング、スライシング、その他のシーケンス的な振舞いをサポートしません。

ほとんどの集合のアプリケーションは、 __hash__() を除いてすべての集合のメソッドを提供する Set クラスを使用します。ハッシュを要求する高度なアプリケーションについては、 ImmutableSet クラスが __hash__() メソッドを加えているが、集合の内容を変更するメソッドは省略されます。 SetImmutableSet は、何が集合であるか決めるのに役立つ (isinstance(obj, BaseSet)) 抽象クラス BaseSet から派生します。

集合クラスは辞書を使用して実装されます。このことから、集合の要素にするには辞書のキーと同様の要件を満たさなければなりません。具体的には、要素になるものには __eq__()__hash__() が定義されているという条件です。その結果、集合はリストや辞書のような変更可能な要素を含むことができません。しかしそれらは、タプルや ImmutableSet のインスタンスのような不変コレクションを含むことができます。集合の集合の実装中の便宜については、内部集合が自動的に変更不可能な形式に変換されます。例えば、 Set([Set(['dog'])])Set([ImmutableSet(['dog'])]) へ変換されます。

class sets.Set([iterable])

新しい空の Set オブジェクトを構築します。もしオプション iterable が与えられたら、イタレータから得られた要素を備えた集合として更新します。 iterable 中の全ての要素は、変更不可能であるか、または 不変に自動変換するためのプロトコル で記述されたプロトコルを使って変更不可能なものに変換可能であるべきです。

class sets.ImmutableSet([iterable])

新しい空の ImmutableSet オブジェクトを構築します。もしオプション iterable が与えられたら、イタレータから得られた要素を備えた集合として更新します。 iterable 中の全ての要素は、変更不可能であるか、または 不変に自動変換するためのプロトコル で記述されたプロトコルを使って変更不可能なものに変換可能であるべきです。

ImmutableSet オブジェクトは __hash__() メソッドを備えているので、集合要素または辞書キーとして使用することができます。 ImmutableSet オブジェクトは要素を加えたり取り除いたりするメソッドを持っていません。したがって、コンストラクタが呼ばれたとき要素はすべて知られていなければなりません。

8.7.1. Set オブジェクト

SetImmutableSet のインスタンスはともに、以下の操作を備えています:

演算 等価な演算 結果
len(s)   集合 s の要素数 (濃度)
x in s   xs に帰属していれば真を返す
x not in s   xs に帰属していなければ真を返す
s.issubset(t) s <= t s のすべての要素が t に帰属していれば真を返す
s.issuperset(t) s >= t t のすべての要素が s に帰属していれば真を返す
s.union(t) s | t st の両方の要素からなる新しい集合
s.intersection(t) s & t st で共通する要素からなる新しい集合
s.difference(t) s - t s にあるが t にない要素からなる新しい集合
s.symmetric_difference(t) s ^ t st のどちらか一方だけに属する要素からなる集合
s.copy()   s の浅いコピーからなる集合

演算子を使わない書き方である union(), intersection(), difference(), および symmetric_difference() は任意のイテレート可能オブジェクトを引数として受け取るのに対し、演算子を使った書き方の方では引数は集合型でなければならないので注意してください。これはエラーの元となる Set('abc') & 'cbs' のような書き方を排除し、より可読性のある Set('abc').intersection('cbs') を選ばせるための仕様です。

バージョン 2.3.1 で変更: 以前は全ての引数が集合型でなければなりませんでした。

加えて、 SetImmutableSet は集合間の比較をサポートしています。二つの集合は、各々の集合のすべての要素が他方に含まれて (各々が他方の部分集合) いる場合、かつその場合に限り等価になります。ある集合は、他方の集合の真の部分集合 (proper subset、部分集合であるが非等価) である場合、かつその場合に限り、他方の集合より小さくなります。ある集合は、他方の集合の真の上位集合 (proper superset、上位集合であるが非等価) である場合、かつその場合に限り、他方の集合より大きくなります。

部分集合比較やと等値比較では、完全な順序決定関数を一般化できません。たとえば、互いに素な 2 つの集合は等しくありませんし、互いの部分集合でもないので、 a<b, a==b, a>bすべて False を返します。したがって集合は __cmp__() メソッドを実装しません。

集合は半順序(部分集合関係)しか定義しないので、集合のリストにおける list.sort() メソッドの出力は未定義です。

以下は ImmutableSet で利用可能であるが Set にはない操作です:

演算 結果
hash(s) s のハッシュ値を返す

以下は Set で利用可能であるが ImmutableSet にはない操作です:

演算 等価な演算 結果
s.update(t) s |= t t を加えた要素からなる集合 s を返します
s.intersection_update(t) s &= t t でも見つかった要素だけを持つ集合 s を返します
s.difference_update(t) s -= t t にあった要素を取り除いた後の集合 s を返します
s.symmetric_difference_update(t) s ^= t st のどちらか一方だけに属する要素からなる集合 s を返します
s.add(x)   要素 x を集合 s に加えます
s.remove(x)   要素 x を集合 s から取り除きます; x がなければ KeyError を送出します
s.discard(x)   要素 x が存在すれば、集合 s から取り除きます
s.pop()   s から任意に要素を取り除き、それを返します; 集合が空なら KeyError を送出します
s.clear()   集合 s からすべての要素を取り除きます

演算子を使わない書き方である update(), intersection_update(), difference_update(), および symmetric_difference_update() は任意のイテレート可能オブジェクトを引数として受け取ることに注目してください。

バージョン 2.3.1 で変更: 以前は全ての引数が集合型でなければなりませんでした。

もう一つ注意を述べますが、このモジュールでは union_update()update() の別名として含まれています。このメソッドは後方互換性のために残されているものです。プログラマは組み込みの set() および frozenset() でサポートされている update() を選ぶべきです。

8.7.2. 使用例

>>> from sets import Set
>>> engineers = Set(['John', 'Jane', 'Jack', 'Janice'])
>>> programmers = Set(['Jack', 'Sam', 'Susan', 'Janice'])
>>> managers = Set(['Jane', 'Jack', 'Susan', 'Zack'])
>>> employees = engineers | programmers | managers           # union
>>> engineering_management = engineers & managers            # intersection
>>> fulltime_management = managers - engineers - programmers # difference
>>> engineers.add('Marvin')                                  # add element
>>> print engineers 
Set(['Jane', 'Marvin', 'Janice', 'John', 'Jack'])
>>> employees.issuperset(engineers)     # superset test
False
>>> employees.update(engineers)         # update from another set
>>> employees.issuperset(engineers)
True
>>> for group in [engineers, programmers, managers, employees]: 
...     group.discard('Susan')          # unconditionally remove element
...     print group
...
Set(['Jane', 'Marvin', 'Janice', 'John', 'Jack'])
Set(['Janice', 'Jack', 'Sam'])
Set(['Jane', 'Zack', 'Jack'])
Set(['Jack', 'Sam', 'Jane', 'Marvin', 'Janice', 'John', 'Zack'])

8.7.3. 不変に自動変換するためのプロトコル

集合は変更不可能な要素だけを含むことできます。都合上、変更可能な Set オブジェクトは、集合要素として加えられる前に、自動的に ImmutableSet へコピーします。

そのメカニズムはハッシュ可能な (hashable) 要素を常に加えることですが、もしハッシュ不可能な場合は、その要素は変更不可能な等価物を返す __as_immutable__() メソッドを持っているかどうかチェックされます。

Set オブジェクトは、 ImmutableSet のインスタンスを返す __as_immutable__() メソッドを持っているので、集合の集合を構築することが可能です。

集合内のメンバーであることをチェックするために、要素をハッシュする必要がある __contains__() メソッドと remove() メソッドが、同様のメカニズムを必要としています。これらのメソッドは要素がハッシュできるかチェックします。もし出来なければ– __hash__(), __eq__(), __ne__() のための一時的なメソッドを備えたクラスによってラップされた要素を返すメソッド– __as_temporarily_immutable__() メソッドをチェックします。

代理メカニズムは、オリジナルの可変オブジェクトから分かれたコピーを組み上げる手間を助けてくれます。

Set オブジェクトは、新しいクラス _TemporarilyImmutableSet によってラップされた Set オブジェクトを返す、 __as_temporarily_immutable__() メソッドを実装します。

ハッシュ可能を与えるための 2 つのメカニズムは通常ユーザーに見えません。しかしながら、マルチスレッド環境下においては、 _TemporarilyImmutableSet によって一時的にラップされたものを持っているスレッドがあるときに、もう一つのスレッドが集合を更新することで、衝突を発生させることができます。言いかえれば、変更可能な集合の集合はスレッドセーフではありません。

8.7.4. 組み込み set 型との比較

組み込みの set および frozenset 型はこの sets で学んだことを生かして設計されています。主な違いは次の通りです。

  • SetImmutableSetsetfrozenset に改名されました。
  • BaseSet に相当するものはありません。代わりに isinstance(x, (set, frozenset)) を使って下さい。
  • 組み込みのものに使われているハッシュアルゴリズムは、多くのデータ集合に対してずっと良い性能 (少ない衝突) を実現します。
  • 組み込みのものはより空間効率良く pickle 化できます。
  • 組み込みのものには union_update() メソッドがありません。代わりに同じ機能の update() メソッドを使って下さい。
  • 組み込みのものには _repr(sorted=True) メソッドがありません。代わりに組み込み関数の repr()sorted() を使って repr(sorted(s)) として下さい。
  • 組み込みのものは変更不可能なものに自動で変換するプロトコルがありません。この機能は多くの人が困惑を覚えるわりに、コミュニティの誰からも実際的な使用例の報告がありませんでした。