今さらPython3 (17) - Set

第3章も佳境?に入ってきました。Setの部分を読み進めています。

入門 Python 3

入門 Python 3

Set()を作る

>>> empty_set = set()
>>> empty_set
set()
>>> even_numbers = {0, 2,4,6,8}
>>> even_numbers
{8, 0, 2, 4, 6}
>>> odd_numbers = {1, 3, 5, 7, 9}
>>> odd_numbers
{9, 1, 3, 5, 7}
>>> 

set名={}だと辞書が出来ちゃうので、set()を使わねばならぬ。順不同なのはdictといっしょ。

>>> set( 'letters' )
{'r', 'l', 'e', 't', 's'}
>>> set(['Dasher', 'Dancer', 'Prancer', 'Mason-Dixon'])
{'Mason-Dixon', 'Prancer', 'Dancer', 'Dasher'}
>>> set(('Ummagumma', 'Echoes', 'Atom Heart Mother'))
{'Atom Heart Mother', 'Echoes', 'Ummagumma'}
>>> set({'apple':'red', 'orange':'orange', 'cherry':'red'})
{'orange', 'apple', 'cherry'}

このあたりは、list()と挙動は同じかな?と思いつつ、辞書(dict)を取り込んだ場合が気になったので、ちと試す。

>>> list({'apple':'red', 'orange':'orange', 'cherry':'red'})
['apple', 'cherry', 'orange']

キーだけが使われるというところは同じですね。

値の有無の確認

まずは辞書を作るところから。

>>> drinks = {
... 'martini': {'vodka', 'vermouth'},
... 'black russian': {'vodka', 'kahlua'}
... , 'white russian': {'cream', 'kahlua', 'vodka'},
... 'manhattan': {'rye', 'vermouth', 'bitters'},
... 'screwdriver:': {'orange juice', 'vodka'}
... }
>>> drinks
{'white russian': {'kahlua', 'vodka', 'cream'}, 'martini': {'vermouth', 'vodka'}, 'black russian': {'kahlua', 'vodka'}, 'screwdriver': {'orange juice', 'vodka'}, 'manhattan': {'vermouth', 'bitters', 'rye'}}
>>> 

インデントはいらないということこに助けられ、行頭にカンマを慌てて入れたのがバレバレ。それはともかく、drinksという辞書(dict)は、キーがカクテルの名前、値の部分がsetになっているわけですね。

>>> for name, contents in drinks.items():
...     if 'vodka' in contents:
...         print(name)
... 
white russian
martini
black russian
screwdriver

if文のところでvermouthかcreamが入っているモノは除外するとこうなる。

>>> for name, contents in drinks.items():
...     if 'vodka' in contents and not ('vermouth' in contents or 'cream' in contents):
...         print(name)
... 
black russian
screwdriver

forでクルクル回して、if文の中でin判定するのはお手軽だけど、理論的には全件検索しているので、件数が多い場合にはどうかな?という疑問は残るよね。

もう少しシンプルに?

>>> drinks = {
... 'martini': {'vodka', 'vermouth'},
... 'black russian': {'vodka', 'kahlua'},
... 'white russian': {'cream', 'kahlua', 'vodka'},
... 'manhattan': {'rye', 'vermouth', 'bitters'},
... 'screwdriver': {'oragne juice', 'vodka'},
... }
>>> drinks
{'black russian': {'kahlua', 'vodka'}, 'white russian': {'kahlua', 'cream', 'vod
ka'}, 'martini': {'vermouth', 'vodka'}, 'manhattan': {'rye', 'vermouth', 'bitter
s'}, 'screwdriver': {'oragne juice', 'vodka'}}
>>> for name, contents in drinks.items():
...     if contents & {'vermouth', 'orange juice'}:
...         print(name)
...
martini
manhattan
>>>

例文を見る限り、&{セット}でOR条件のように作用しているみたい。ただ、なんでscrewdriverが選ばれないが不思議だと思っていたら、orangeのスペル間違ってた。。。

>>> drinks['screwdriver'] = {'orange juice', 'vodka'}
>>> drinks
{'black russian': {'kahlua', 'vodka'}, 'white russian': {'kahlua', 'cream', 'vod
ka'}, 'martini': {'vermouth', 'vodka'}, 'manhattan': {'rye', 'vermouth', 'bitter
s'}, 'screwdriver': {'orange juice', 'vodka'}}
>>> for name, contents in drinks.items():
...     if contents & {'vermouth', 'orange juice'}:
...         print(name)
...
martini
manhattan
screwdriver
>>>

vodkaは入っているけど、creamやvermouthが入っているのは除外するときはこんな。

>>> for name, contents in drinks.items():
...     if 'vodka' in contents and not contents & {'vermouth', 'cream'}:
...         print(name)
...
black russian
screwdriver

辞書のキーを指定すると値が変数に渡される。今回の場合は、辞書の値部分がセットになっているだけだよね。

>>> buss = drinks['black russian']
>>> wruss = drinks['white russian']
>>> buss
{'kahlua', 'vodka'}
>>> wruss
{'kahlua', 'cream', 'vodka'}
>>>

ここで集合の話?

>>> a = {1, 2}
>>> b = {2, 3}
>>> a & b
{2}
>>> a.intersection(b)
{2}

これをやると、さっきみたカクテルの中身のところで、contents & {セット}がOR条件ぽくなっているところが気になる訳で、もう1回。

>>> for name, contents in drinks.items():
...     if contents & {'vermouth', 'orange juice'}:
...         print(name)
...
martini
manhattan
screwdriver
>>>

contentsには、カクテルの材料のsetが入っていて、後ろのセットにはvermouth, orange juiceのsetが入っている。両方に共通する要素(intersection)が1つでもあるならば、if文はTrueを返すので、見た目はOR条件ぽく見えると理解すれば良さそう。

>>> buss = drinks['black russian']
>>> wruss = drinks['white russian']
>>> bruss
{'kahlua', 'vodka'}
>>> wruss
{'kahlua', 'cream', 'vodka'}
>>> bruss & wruss
{'kahlua', 'vodka'}

さっきのところが理解できていれば、ここの部分もすんなり理解できる。

日本語訳がセットではなくて集合となっている理由

だんだん分かってきた。

>>> a | b
{1, 2, 3}
>>> a.union(b)
{1, 2, 3}
>>> bruss | wruss
{'kahlua', 'cream', 'vodka'}
>>>

aとbに入るものすべて(union)を表すのが|ですと。

>>> a - b
{1}
>>> a.difference(b)
{1}
>>> a
{1, 2}
>>> b
{2, 3}
>>> bruss-wruss
set()
>>> wruss - bruss
{'cream'}
>>> wruss
{'kahlua', 'cream', 'vodka'}
>>> bruss
{'kahlua', 'vodka'}
>>>

こっちは引き算で、aからbに属しているものを引き去ったらという話だね。

>>> a ^ b
{1, 3}
>>> a.symmetric_difference(b)
{1, 3}
>>> bruss ^ wruss
{'cream'}

これを使う機会がどのぐらいあるか分からないけど、aとbの集合から両者に重複したものを取り去ったパターン。

サブセットですか?

>>> a <= b
False
>>> a.issubset(b)
False
>>> bruss <=wruss
True
>>> a<=a
True
>>> a.issubset(a)
True
>>>

余り使うシーンが浮かばないけど、<=じゃなくて<だと完全に同じではないサブセットだよという判定ができると理解。

>>> a < b
False
>>> a < a
False
>>> bruss < wruss
True

サブセットじゃなかったら、今度はスーパーセットですかという確認をする事も出来る。

>>> a >= b
False
>>> a.issuperset(b)
False
>>> wruss >= bruss
True
>>> a >= a
True
>>> a.issuperset(a)
True
>>> a > b
False
>>> wruss > bruss
True
>>> a > a
False

ま、サブセットの時と同じですね。

(つづく)