defaultdict を試してみた

  • 12月 20, 2008 08:55

ちょっと、コードを読んでいたら python2.5から登場したdefaultdictが出てきたので調べてみました。

普通の辞書だとkeyエラーを防ぐためにsetdefaultなど使うのですが、

>>> standard_dict = {}
>>> [standard_dict.setdefault(i, i*3 ) for i in range(5)]
[0, 3, 6, 9, 12]
>>> standard_dict
{0: 0, 1: 3, 2: 6, 3: 9, 4: 12}

>>> standard_dict[6] += 5 # keyがないためにエラー
---------------------------------------------------------------------------
<type 'exceptions.KeyError'>              Traceback (most recent call last)
<type 'exceptions.KeyError'>: 6
>>> standard_dict.setdefault(6, 0) += 5 # これはstandard_dict.setdefault(6, 0)が0を返しちゃうのでエラー
------------------------------------------------------------
   File "<ipython console>", line 1
<type 'exceptions.SyntaxError'>: illegal expression for augmented assignment (<ipython console>, line 1)
# keyがあるかないか分らない場合は、このように書くことが多い(ちなみにtry exceptの方が早いらしい)
>>> standard_dict.setdefault(6, 0)
>>> standard_dict[6] += 5
>>> standard_dict
{0: 0, 1: 3, 2: 6, 3: 9, 4: 12, 6: 5}

ところがdefaultdictを使うと

>>> from collections import defaultdict #python2.5以上
>>> default_dict = defaultdict(int, ((i, i*3,) for i in range(5))) # 第2引数は(key, value)のタプルのiterableのオブジェクト
>>> default_dict
defaultdict(<type 'int'>, {0: 0, 1: 3, 2: 6, 3: 9, 4: 12})

#これが大丈夫
>>> default_dict[6] += 5
>>> default_dict
defaultdict(<type 'int'>, {0: 0, 1: 3, 2: 6, 3: 9, 4: 12, 6: 5})

ただ、intを渡さないとエラーになるわけではないし、int型を保証しているわけでもないようです。

>>> d = defaultdict(int, ((i, i,) for i in "abcde"))
>>> d
defaultdict(<type 'int'>, {'a': 'a', 'c': 'c', 'b': 'b', 'e': 'e', 'd': 'd'})

# でもこれはOK
>>> d['f'] += 44
>>> d
defaultdict(<type 'int'>, {'a': 'a', 'c': 'c', 'b': 'b', 'e': 'e', 'd': 'd', 'f': 44})

辞書型のgetやsetdefaultの処理は重いらしいので、defaultdictを使った方が良いらしいです。

次にitertoolsのcycleを使って、

  • 第二引数で渡した値は(key)-yes, (key)-no を繰り返すiterableなオブジェクトをvalue
  • あとから追加したものはyes, noを繰り返すiterableなオブジェクトをvalue

にするdefaultdictを作るときは

>>> from itertools import cycle
>>> yesno_dict = defaultdict(lambda:cycle(("yes", "no",)),
((i, cycle(("%s-yes" % i, "%s-no" % i,))) for i in "abc"))

#keyにcがあるので、c-yes,c-noを繰り返すiterableなオブジェクトを返す
>>> i = 0
>>> for b in yesno_dict['c']:
    if i > 10:break
    print b
    i += 1
   ....:
   ....:
c-yes
c-no
c-yes
c-no
c-yes
c-no
c-yes
c-no
c-yes
c-no
c-yes
c-no

#keyにdはないため、yes,noを繰り返すiterableなオブジェクトを返す
>>> i = 0
>>> for b in yesno_dict['d']:
    if i > 10:break
    print b
    i += 1
   ....:
   ....:
yes
no
yes
no
yes
no
yes
no
yes
no
yes
no

読んだコードでは、行の色をcssで変更するために使っているようでした。

便利と言えば便利??