Python基礎講座 #11 セット (Set)
小学校の算数の時間に集合について学んだことを覚えていますか。学校を卒業されてあまり経っていなければはっきり覚えていらっしゃるでしょうし、卒業されてからずいぶん経っていれば記憶があいまいだと思いますが、いくら記憶がはっきりしなくても、和集合、積集合、補集合、差集合といった様々な集合の形を学ばれたはずです。Pythonにもこの集合に似たデータ構造が用意されており、それがセットです。それでは今からセットについて勉強していきましょう。
まず、セットオブジェクトを生成する方法を見ていきます。セットを生成する方法は2つあります。
1つ目の方法は、セットコンストラクタを使う方法です。セットコンストラクタはシーケンス型のオブジェクトを1つだけ引数として受け取り、もし引数を受け取らなければ空のセットオブジェクトを返します。
リストをコンストラクタの引数として渡してセットを生成したあと、type関数を使ってオブジェクトの型を確認してみましょう。
>>> set_1 = set([1, 2, 3])
>>> print(set_1)
{1, 2, 3}
>>> print(type(set_1))
<class 'set'>セットオブジェクトが生成されたことを確認しました。
今度はタプルを引数として渡して生成してみましょう。
>>> set_2 = set((1, 2, 3))
>>> print(set_2)
{1, 2, 3}
>>> print(type(set_2))
<class 'set'>リストを使ったときと同じようにセットオブジェクトが生成されました。
今度は文字列オブジェクトとrangeオブジェクトを引数として渡してみましょう。
>>> set_3 = set('string')
>>> print(set_3)
{'s', 't', 'n', 'r', 'i', 'g'}
>>> print(type(set_3))
<class 'set'>
>>> set_4 = set(range(10))
>>> print(set_4)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> print(type(set_4))
<class 'set'>文字列とrangeオブジェクトを使って、それぞれのセットが生成されました。
今度はコンストラクタを引数なしで実行して、空のセットを生成してみましょう。
>>> set_5 = set()
>>> print(set_5)
set()
>>> print(type(set_5))
<class 'set'>空のセットが生成されました。
前にも説明したように、セットコンストラクタはリスト、タプル、文字列、rangeなどのシーケンス型のオブジェクトを1つだけ引数として受け取ります。もしシーケンス型ではないオブジェクトを渡すとどうなるか確認してみましょう。
シーケンスオブジェクトではない、それぞれの数値1、2、3を引数として渡してみます。
>>> set(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: set expected at most 1 argument, got 3セットは1つの引数しか受け取らないのに、3つの引数が渡されたというエラーが発生するのが分かります。
今度はintオブジェクトを1つだけ引数として渡してみましょう。
>>> set(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterableintはiterableオブジェクトではないというエラーが発生しました。
セットを生成する2つ目の方法は、波括弧を使う方法です。波括弧を使う方式は、リストやタプルを生成する方式と似ています。
波括弧を使ってセットを生成してみましょう。
>>> set_6 = {1, 2, 3}
>>> print(set_6)
{1, 2, 3}
>>> print(type(set_6))
<class 'set'>コンストラクタを使ったときと同じようにセットが生成されました。ところで、空のセットを生成するために空の波括弧を使うとどうなるでしょうか。そうです、辞書の講座で学んだように、空の波括弧を使うと空の辞書が生成されます。
実際に実行してみましょう。
>>> var = {}
>>> print(var)
{}
>>> print(type(var))
<class 'dict'>空の波括弧を使うと、空のセットではなく空の辞書が生成されるのが分かります。この点はぜひ覚えておいてください。
次に、セットの特徴について見ていきましょう。
セットの1つ目の特徴です。セットは順序を持たないデータ型です。
セットを1つ生成して確認してみましょう。
>>> my_set = {'one', 'two', 'three'}
>>> my_set
{'three', 'one', 'two'}アイテムの順序が、定義した順序とは異なって生成されているのが分かります。
新しいアイテムを1つ追加してみましょう。
>>> my_set.add('four')
>>> my_set
{'four', 'three', 'one', 'two'}新しいアイテムが最後の位置ではなく、別の位置に追加されたのが分かります。
セットの2つ目の特徴です。セットは順序を持たないデータ型なので、インデックスを使うことができません。
インデックスを使ってみましょう。
>>> my_set[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'set' object is not subscriptableセットオブジェクトはインデックスをサポートしないというエラーメッセージが出力されました。
セットの3つ目の特徴です。セットは同じ値を持つアイテムを1つしか許容しません。
同じ値の数値2つを使ってセットを生成してみましょう。
>>> my_set = {1, 2, 2, 3}
>>> my_set
{1, 2, 3}数値2が1つだけ残っているのが分かります。
今生成したセットオブジェクトは1の値を持つアイテムを持っていますが、新しい1をaddメソッドで追加してみましょう。
>>> my_set.add(1)
>>> my_set
{1, 2, 3}数値1はすでにセットの中に存在するので、追加されていないことが分かります。セットは重複した値を除外するため、あるデータからユニークな値だけを抽出するのに便利に使われます。
セットの4つ目の特徴です。セットは一度定義すると変更が不可能なイミュータブル (immutable) 型のみアイテムとして使えます。イミュータブル型にはboolean、int、float、tuple、str、frozensetなどがあります。逆に変更が可能なミュータブル型にはlist、set、dictionaryなどがあります。したがって、list、set、dictionaryはセットのアイテムとして使えません。
イミュータブル型であるタプルをアイテムとして持つセットを定義してみましょう。
>>> {(1, 2, 3), (2, 3, 4)}
{(1, 2, 3), (2, 3, 4)}タプルを使って問題なく生成されることを確認しました。
今度はミュータブル型であるリストをアイテムとして使って定義してみましょう。
>>> {[1, 2, 3], [2, 3, 4]}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'リストはunhashable型だという型エラーが発生しました。unhashable型はミュータブル型と同じだと考えていただければと思います。
最後に、セットクラスが提供するメソッドを見ていきましょう。
helpコマンドを使って確認してみましょう。
>>> help(set)
Help on class set in module builtins:
class set(object)
| set() -> new empty set object
| set(iterable) -> new set object
|
| Build an unordered collection of unique elements.
|
| Methods defined here:
|
| __and__(self, value, /)
| Return self&value.
|
| __contains__(...)
| x.__contains__(y) <==> y in x.
|
| __eq__(self, value, /)
| Return self==value.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __gt__(self, value, /)
| Return self>value.
|
| __iand__(self, value, /)
| Return self&=value.
|
| __init__(self, /, *args, **kwargs)
| Initialize self. See help(type(self)) for accurate signature.
|
| __ior__(self, value, /)
| Return self|=value.
|
| __isub__(self, value, /)
| Return self-=value.
|
| __iter__(self, /)
| Implement iter(self).
|
| __ixor__(self, value, /)
| Return self^=value.
|
| __le__(self, value, /)
| Return self<=value.
|
| __len__(self, /)
| Return len(self).
|
| __lt__(self, value, /)
| Return self<value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __or__(self, value, /)
| Return self|value.
|
| __rand__(self, value, /)
| Return value&self.
|
| __reduce__(...)
| Return state information for pickling.
|
| __repr__(self, /)
| Return repr(self).
|
| __ror__(self, value, /)
| Return value|self.
|
| __rsub__(self, value, /)
| Return value-self.
|
| __rxor__(self, value, /)
| Return value^self.
|
| __sizeof__(...)
| S.__sizeof__() -> size of S in memory, in bytes
|
| __sub__(self, value, /)
| Return self-value.
|
| __xor__(self, value, /)
| Return self^value.
|
| add(...)
| Add an element to a set.
|
| This has no effect if the element is already present.
|
| clear(...)
| Remove all elements from this set.
|
| copy(...)
| Return a shallow copy of a set.
|
| difference(...)
| Return the difference of two or more sets as a new set.
|
| (i.e. all elements that are in this set but not the others.)
|
| difference_update(...)
| Remove all elements of another set from this set.
|
| discard(...)
| Remove an element from a set if it is a member.
|
| If the element is not a member, do nothing.
|
| intersection(...)
| Return the intersection of two sets as a new set.
|
| (i.e. all elements that are in both sets.)
|
| intersection_update(...)
| Update a set with the intersection of itself and another.
|
| isdisjoint(...)
| Return True if two sets have a null intersection.
|
| issubset(...)
| Report whether another set contains this set.
|
| issuperset(...)
| Report whether this set contains another set.
|
| pop(...)
| Remove and return an arbitrary set element.
| Raises KeyError if the set is empty.
|
| remove(...)
| Remove an element from a set; it must be a member.
|
| If the element is not a member, raise a KeyError.
|
| symmetric_difference(...)
| Return the symmetric difference of two sets as a new set.
|
| (i.e. all elements that are in exactly one of the sets.)
|
| symmetric_difference_update(...)
| Update a set with the symmetric difference of itself and another.
|
| union(...)
| Return the union of sets as a new set.
|
| (i.e. all elements that are in either set.)
|
| update(...)
| Update a set with the union of itself and others.
|
| ----------------------------------------------------------------------
| Class methods defined here:
|
| __class_getitem__(...) from builtins.type
| See PEP 585
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __hash__ = Noneセットクラスはたくさんのメソッドを提供していますが、この中でもよく使うメソッドをいくつか取り上げて使い方を学んでいきましょう。
まずaddメソッドから見ていきましょう。
>>> help(set.add)
Help on method_descriptor:
add(...)
Add an element to a set.
This has no effect if the element is already present.addメソッドはセットにアイテムを追加するときに使います。前述のとおり、すでに存在する値は重複して追加されません。
空のセットを1つ生成したあと、addメソッドを使ってアイテムを追加してみましょう。
>>> myset = set()
>>> print(myset)
set()
>>>
>>> myset.add(1)
>>> print(myset)
{1}
>>>
>>> myset.add(2)
>>> print(myset)
{1, 2}空のセットに1と2が追加されたことを確認しました。
次はpopメソッドです。
>>> help(set.pop)
Help on method_descriptor:
pop(...)
Remove and return an arbitrary set element.
Raises KeyError if the set is empty.popメソッドは任意のアイテムを削除します。もしセットにアイテムが存在しなければ、KeyErrorを発生させます。
popメソッドを使ってみましょう。
>>> myset.pop()
1
>>> print(myset)
{2}アイテム1を返しながら、セットの中から削除されたのが分かります。
もう一度実行してみましょう。
>>> myset.pop()
2
>>> print(myset)
set()アイテム2が返されて、空のセットが残るのが分かります。
アイテムがない状態でpopメソッドを実行してみましょう。
>>> myset.pop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'pop from an empty set'アイテムがない状態でpopメソッドを実行すると、KeyErrorが発生することを確認しました。
次はremoveメソッドについて見ていきましょう。
>>> help(set.remove)
Help on method_descriptor:
remove(...)
Remove an element from a set; it must be a member.
If the element is not a member, raise a KeyError.removeメソッドもpopメソッドと同じく、アイテムを削除するときに使います。ただしpopメソッドが引数を受け取らず任意のアイテムを削除するのに対し、removeメソッドは削除するアイテムを引数として受け取り、受け取った引数がセットのメンバーアイテムでなければKeyErrorを発生させます。
セットを再定義したあと、removeメソッドを使ってみましょう。
>>> myset = {1, 2, 3}
>>> myset.remove(1)
>>> print(myset)
{2, 3}removeメソッドを使ってアイテム1が削除されたことを確認しました。
今度はアイテムとして持っていない値を引数として使ってみましょう。
>>> myset.remove(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 4KeyErrorが発生することを確認しました。
次はunionメソッドについて見ていきましょう。
>>> help(set.union)
Help on method_descriptor:
union(...)
Return the union of sets as a new set.
(i.e. all elements that are in either set.)前にセットは算数の集合に似たデータ型だと説明しました。2つ以上のセットに対して和集合、積集合、補集合のようなデータを抽出することができますが、下の図のような和集合を抽出するときに使われるメソッドがunionメソッドです。
まずunionメソッドを使うために、セットオブジェクトを2つ生成してみましょう。
>>> set_1 = {1, 2, 3}
>>> set_2 = {3, 4, 5}unionメソッドを使って2つのセットの和集合を求めてみましょう。
>>> set_1.union(set_2)
{1, 2, 3, 4, 5}今度は下の図の積集合のように、2つのセットに共通するアイテムだけを返すintersectionメソッドを使ってみましょう。
intersectionメソッドを使って2つのセットの積集合を出力してみましょう。
>>> set_1.intersection(set_2)
{3}共通アイテムである3だけが返されたのが分かります。
次は下の図の差集合のように、片方のセットからもう一方のセットのアイテムを取り除いたアイテムだけを返すdifferenceメソッドを実行してみましょう。
>>> set_1.difference(set_2)
{1, 2}セット2のアイテムである3が取り除かれて、1と2だけが返されたのが分かります。
セットクラスが提供するメソッドには、ここで紹介したもの以外にも便利なメソッドがありますので、ぜひ実際に使ってみてください。セットはリストや辞書のように非常に頻繁に使うデータ構造ではありませんが、必要なときにとても便利に使えるデータ構造ですので、しっかり習得しておいてください。
次の講座では、プログラムを制御するために最もよく使われる制御文の1つである、if条件文について学んでいきます。
ありがとうございました。