setは、ハッシュ可能なオブジェクトを集めた、_変更可能_で_順序を持たない_コレクションです。集合の要素は互いに異なっている必要があり、重複する要素は許されません。複数の異なるデータ型や、tupleのtuplesのような入れ子の構造も、すべての要素が_ハッシュ可能_であれば保持できます。集合には、変更できないfrozensetという種類もあります。
集合は、他のデータ構造や要素のまとまりから重複をすばやく取り除くためによく使われます。順序付けや重複の追跡が必要ない場面での、効率的な比較にも使われます。
setは、他のコレクション型(辞書、リスト、タプル)と同じく、次のことをサポートします。
for item in <set>による繰り返しinとnot inによる要素の確認len()による長さの取得copy()による浅いコピーsetsは次のことをサポートしません。
+による連結setに要素が含まれているかを確認するときの時間計算量は、(平均して)一定です。一方、listやstringに含まれているかを確認する場合は、データの長さが増えるにつれて時間計算量も大きくなります。<set>.union()、<set>.intersection()、<set>.difference()などのメソッドも、(平均して)一定の時間計算量です。
集合を作る方法はいろいろありますが、もっともわかりやすいのは、_集合リテラル_を書く方法、setクラスのコンストラクター(set())を使う方法、そして_集合内包表記_を使う方法です。
setは、_集合リテラル_として、波括弧({})を使い、要素をカンマで区切って直接書くことができます。重複した要素は黙って取り除かれます。
>>> one_element = {'➕'}
{'➕'}
>>> multiple_elements = {'➕', '🔻', '🔹', '🔆'}
{'➕', '🔻', '🔹', '🔆'}
>>> multiple_duplicates = {'Hello!', 'Hello!', 'Hello!',
'¡Hola!','Привіт!', 'こんにちは!',
'¡Hola!','Привіт!', 'こんにちは!'}
{'こんにちは!', '¡Hola!', 'Hello!', 'Привіт!'}
集合リテラルはdictリテラルと同じ波括弧を使うので、空のsetを作るにはset()を使う必要があります。
set()(setクラスのコンストラクター)は、引数に渡した任意のiterableに対して使えます。iterableの要素は1つずつ順に取り出され、setに追加されます。要素の順序は保持されず、重複は黙って取り除かれます。
# To create an empty set, the constructor must be used.
>>> no_elements = set()
set()
# The tuple is unpacked & each element is added.
# Duplicates are removed.
>>> elements_from_tuple = set(("Parrot", "Bird",
334782, "Bird", "Parrot"))
{334782, 'Bird', 'Parrot'}
# The list is unpacked & each element is added.
# Duplicates are removed.
>>> elements_from_list = set([2, 3, 2, 3, 3, 3, 5,
7, 11, 7, 11, 13, 13])
{2, 3, 5, 7, 11, 13}
listsやdictsと同じように、集合も_内包表記_で作ることができます。
# First, a list with duplicates
>>> numbers = [1,2,3,4,5,6,6,5,4,8,9,9,9,2,3,12,18]
# This set comprehension squares the numbers divisible by 3
# Duplicates are removed.
>>> calculated = {item**2 for item in numbers if item % 3 == 0}
{9, 36, 81, 144, 324}
setコンストラクターは「展開」という挙動をするため、文字列に対して使うと思いがけない結果になることがあります。
# String elements (Unicode code points) are
# iterated through and added *individually*.
>>> elements_string = set("Timbuktu")
{'T', 'b', 'i', 'k', 'm', 't', 'u'}
# Unicode separators and positioning code points
# are also added *individually*.
>>> multiple_code_points_string = set('अभ्यास')
{'अ', 'भ', 'य', 'स', 'ा', '्'}
覚えておきましょう。集合はさまざまなデータ型や_入れ子_のデータ型を保持できますが、setの要素はすべて_ハッシュ可能_でなければなりません。
# Attempting to use a list for a set member throws a TypeError
>>> lists_as_elements = {['🌈','💦'],
['☁️','⭐️','🌍'],
['⛵️', '🚲', '🚀']}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
# Standard sets are mutable, so they cannot be hashed.
>>> sets_as_elements = {{'🌈','💦'},
{'☁️','⭐️','🌍'},
{'⛵️', '🚲', '🚀'}}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
ただし、frozenset()型を使えば、setsのsetを作ることができます。
# Frozensets don't have a literal form.
>>> set_1 = frozenset({'🌈','💦'})
>>> set_2 = frozenset({'☁️','⭐️','🌍'})
>>> set_3 = frozenset({'⛵️', '🚲', '🚀'})
>>> frozen_sets_as_elements = {set_1, set_2, set_3}
>>> frozen_sets_as_elements
{frozenset({'⛵️', '🚀', '🚲'}),
frozenset({'🌈', '💦'}),
frozenset({'☁️', '⭐️', '🌍'})}
setの要素は、<set>.add(<item>)と<set>.remove(<item>)というメソッドで追加したり削除したりできます。.remove(<item>)メソッドは、指定した要素がsetにない場合にKeyErrorを送出します。
>>> creatures = {'crow', 'duck', 'fish', 'monkey', 'elephant'}
>>> creatures.add('beaver')
>>> creatures.remove('duck')
>>> creatures
{'beaver', 'crow', 'elephant', 'fish', 'monkey'}
# Trying to remove an item that is not present raises a KeyError
>>> creatures.remove('bear')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'bear'
<set>.discard(<item>)はsetから要素を削除しますが、その要素がない場合でもKeyErrorを送出しません。<set>.clear()は集合からすべての要素を削除します。<set>.pop()は任意の要素を削除して_返します_。setが空の場合はKeyErrorを送出します。集合には、一般に数学の集合演算を模したメソッドがあります。これらのメソッドのほとんど(すべてではありません)には、対応する演算子があります。メソッドは通常、任意のiterableを引数に取りますが、演算子を使うには、演算の両辺がsetまたはfrozensetである必要があります。
<set>.isdisjoint(<other_collection>)メソッドは、あるsetの要素が別の集合の要素と少しでも重なっているかを調べるために使います。このメソッドは、任意のiterableやsetを引数に取ります。2つの集合に共通する要素がまったくない場合はTrueを、要素を共有している場合はFalseを返します。
# Both mammals and additional_animals are lists.
>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
>>> additional_animals = ['pangolin', 'panda', 'parrot',
'lemur', 'tiger', 'pangolin']
# Animals is a dict.
>>> animals = {'chicken': 'white',
'sparrow': 'grey',
'eagle': 'brown and white',
'albatross': 'grey and white',
'crow': 'black',
'elephant': 'grey',
'dog': 'rust',
'cow': 'black and white',
'tiger': 'orange and black',
'cat': 'grey',
'squirrel': 'black'}
# Birds is a set.
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
# Mammals and birds don't share any elements.
>>> birds.isdisjoint(mammals)
True
# There are also no shared elements between
# additional_animals and birds.
>>> birds.isdisjoint(additional_animals)
True
# Animals and mammals have shared elements.
# **Note** The first object needs to be a set or converted to a set
# since .isdisjoint() is a set method.
>>> set(animals).isdisjoint(mammals)
False
<set>.issubset(<other_collection>)は、<set>のすべての要素が<other_collection>にも含まれているかを調べるために使います。演算子を使うと<set> <= <other_set>となります。
# Set methods will take any iterable as an argument.
# All members of birds are also members of animals.
>>> birds.issubset(animals)
True
# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issubset() is a set method.
>>> set(mammals).issubset(animals)
True
# Both objects need to be sets to use a set operator
>>> birds <= set(mammals)
False
# A set is always a loose subset of itself.
>>> set(additional_animals) <= set(additional_animals)
True
<set>.issuperset(<other_collection>)は.issubset()の逆です。<other_collection>のすべての要素が<set>にも含まれているかを調べるために使います。演算子を使うと<set> >= <other_set>となります。
# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issuperset() is a set method.
>>> set(animals).issuperset(mammals)
True
# All members of animals do not show up as members of birds.
>>> birds.issuperset(animals)
False
# Both objects need to be sets to use a set operator
>>> birds >= set(mammals)
False
# A set is always a loose superset of itself.
>>> set(animals) >= set(animals)
True
<set> < <other_set>と<set> > <other_set>は、_真部分集合_かどうかを調べるために使います。<演算子では、(<set> <= <other_set>) かつ(<set> != <other_set>)であれば、そのsetは真部分集合です。
>演算子では、(<set> >= <other_set>) かつ(<set> != <other_set>)であれば、そのsetは真の上位集合です。これらの演算子には、対応するメソッドがありません。
>>> animal_names = {'albatross','cat','chicken','cow','crow','dog',
'eagle','elephant','sparrow','squirrel','tiger'}
>>> animals_also = {'albatross','cat','chicken','cow','crow','dog',
'eagle','elephant','sparrow','squirrel','tiger'}
>>> mammals = {'squirrel','dog','cat','cow', 'tiger', 'elephant'}
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
>>> mammals < animal_names
True
>>> animal_names > birds
True
# A set is not a *proper subset* if set == other set.
>>> animals_also < animal_names
False
# A set is never a *proper subset* of itself
>>> animals_also < animals_also
False
<set>.union(*<other iterables>)は、<set>とすべての<other iterables>の要素を持つ新しいsetを返します。このメソッドの演算子による書き方は、<set> | <other set 1> | <other set 2> | ... | <other set n>です。
>>> perennials = {'Asparagus', 'Broccoli', 'Sweet Potato', 'Kale'}
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Summer Squash'}
>>> more_perennials = ['Radicchio', 'Rhubarb',
'Spinach', 'Watercress']
# Methods will take any iterable as an argument.
>>> perennials.union(more_perennials)
{'Asparagus','Broccoli','Kale','Radicchio','Rhubarb',
'Spinach','Sweet Potato','Watercress'}
# Operators require sets.
>>> set(more_perennials) | perennials
{'Asparagus',
'Broccoli',
'Kale',
'Radicchio',
'Rhubarb',
'Spinach',
'Sweet Potato',
'Watercress'}
<set>.difference(*<other iterables>)は、元の<set>の要素のうち、<others>にない要素を持つ新しいsetを返します。このメソッドの演算子による書き方は、<set> - <other set 1> - <other set 2> - ... - <other set n>です。
>>> berries_and_veggies = {'Asparagus',
'Broccoli',
'Watercress',
'Goji Berries',
'Goose Berries',
'Ramps',
'Walking Onions',
'Blackberries',
'Strawberries',
'Rhubarb',
'Kale',
'Artichokes',
'Currants'}
>>> veggies = ('Asparagus', 'Broccoli', 'Watercress', 'Ramps',
'Walking Onions', 'Rhubarb', 'Kale', 'Artichokes')
# Methods will take any iterable as an argument.
>>> berries = berries_and_veggies.difference(veggies)
{'Blackberries','Currants','Goji Berries',
'Goose Berries', 'Strawberries'}
# Operators require sets.
>>> berries_and_veggies - berries
{'Artichokes','Asparagus','Broccoli','Kale',
'Ramps','Rhubarb','Walking Onions','Watercress'}
<set>.intersection(*<other iterables>)は、元のsetとすべての<others>に共通する要素を持つ新しいsetを返します(言い換えると、すべてが交わるsetです)。このメソッドの演算子による書き方は、<set> & <other set> & <other set 2> & ... & <other set n>です。
>>> perennials = {'Annatto','Asafetida','Asparagus','Azalea',
'Winter Savory', 'Broccoli','Curry Leaf','Fennel',
'Kaffir Lime','Kale','Lavender','Mint','Oranges',
'Oregano', 'Tarragon', 'Wild Bergamot'}
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Marjoram',
'Summer Squash', 'Okra','Shallots', 'Basil',
'Cilantro', 'Cumin', 'Sunflower', 'Chervil',
'Summer Savory'}
>>> herbs = ['Annatto','Asafetida','Basil','Chervil','Cilantro',
'Curry Leaf','Fennel','Kaffir Lime','Lavender',
'Marjoram','Mint','Oregano','Summer Savory',
'Tarragon','Wild Bergamot','Wild Celery',
'Winter Savory']
# Methods will take any iterable as an argument.
>>> perennial_herbs = perennials.intersection(herbs)
{'Annatto', 'Asafetida', 'Curry Leaf', 'Fennel', 'Kaffir Lime',
'Lavender', 'Mint', 'Oregano', 'Wild Bergamot','Winter Savory'}
# Operators require both groups be sets.
>>> annuals & set(herbs)
{'Basil', 'Chervil', 'Marjoram', 'Cilantro'}
<set>.symmetric_difference(<other iterable>)は、<set>または<other>のどちらか一方にあり、両方にはない要素を持つ新しいsetを返します。このメソッドの演算子による書き方は、<set> ^ <other set>です。
>>> plants_1 = {'🌲','🍈','🌵', '🥑','🌴', '🥭'}
>>> plants_2 = ('🌸','🌴', '🌺', '🌲', '🌻', '🌵')
# Methods will take any iterable as an argument.
>>> fruit_and_flowers = plants_1.symmetric_difference(plants_2)
>>> fruit_and_flowers
{'🌸', '🌺', '🍈', '🥑', '🥭','🌻' }
# Operators require both groups be sets.
>>> fruit_and_flowers ^ plants_1
{'🌲', '🌸', '🌴', '🌵','🌺', '🌻'}
>>> fruit_and_flowers ^ set(plants_2)
{'🥭', '🌴', '🌵', '🍈', '🌲', '🥑'}
3つ以上の集合の対称差を取ると、結果のsetには、それぞれのsetに固有の要素と、その中の3つ以上の集合で共有されている要素の両方が含まれます(詳しくはWikipediaのsymmetric differenceの記事を参照してください)。
その中のそれぞれのsetsに固有の要素だけを取り出すには、2つの集合のすべての組み合わせの積集合を別の手順で集めてから、取り除く必要があります。
>>> one = {'black pepper','breadcrumbs','celeriac','chickpea flour',
'flour','lemon','parsley','salt','soy sauce',
'sunflower oil','water'}
>>> two = {'black pepper','cornstarch','garlic','ginger',
'lemon juice','lemon zest','salt','soy sauce','sugar',
'tofu','vegetable oil','vegetable stock','water'}
>>> three = {'black pepper','garlic','lemon juice','mixed herbs',
'nutritional yeast', 'olive oil','salt','silken tofu',
'smoked tofu','soy sauce','spaghetti','turmeric'}
>>> four = {'barley malt','bell pepper','cashews','flour',
'fresh basil','garlic','garlic powder', 'honey',
'mushrooms','nutritional yeast','olive oil','oregano',
'red onion', 'red pepper flakes','rosemary','salt',
'sugar','tomatoes','water','yeast'}
>>> intersections = (one & two | one & three | one & four |
two & three | two & four | three & four)
...
{'black pepper','flour','garlic','lemon juice','nutritional yeast',
'olive oil','salt','soy sauce', 'sugar','water'}
# The ^ operation will include some of the items in intersections,
# which means it is not a "clean" symmetric difference - there
# are overlapping members.
>>> (one ^ two ^ three ^ four) & intersections
{'black pepper', 'garlic', 'soy sauce', 'water'}
# Overlapping members need to be removed in a separate step
# when there are more than two sets that need symmetric difference.
>>> (one ^ two ^ three ^ four) - intersections
...
{'barley malt','bell pepper','breadcrumbs', 'cashews','celeriac',
'chickpea flour','cornstarch','fresh basil', 'garlic powder',
'ginger','honey','lemon','lemon zest','mixed herbs','mushrooms',
'oregano','parsley','red onion','red pepper flakes','rosemary',
'silken tofu','smoked tofu','spaghetti','sunflower oil', 'tofu',
'tomatoes','turmeric','vegetable oil','vegetable stock','yeast'}