python 便利なcollectionsライブラリ、個人的使えるもの順紹介

マイクロアドアドベントカレンダー三日目担当の記事です。

Pythonは単純でわかりやすいプログラミング言語のため、欲しいデータ構造を自ら実装してしまうことが良くありますが、標準ライブラリに目を通すと多くのデータ構造はすでに用意されている事が多いです。
今回は自戒を込めてPython3のcollectionsライブラリについて紹介します。

個人的に使えると感じた順番で紹介します。
* Counter
* defaultdict
* deque
* OrderedDict
* namedtuple
* ChainMap

Counter

リストやイテレータから値の出現回数をカウントします。

>>> import collections from Counter
>>> Counter([1,1,2,3,4,4,4,5])
Counter({4: 3, 1: 2, 2: 1, 3: 1, 5: 1})
>>> Counter("Qiita")  # str型はイテレータ
Counter({'i': 2, 'Q': 1, 't': 1, 'a': 1})

データのたくさん入ったCSVなどをPythonを利用して集計することがあるのですが
今までデータの出現回数を出したい時には辞書型とfor文を組み合わせてカウントしていました・・・。
Counterを導入するとコードが短く、見やすくなったため便利なデータ構造です。

defaultdict

存在しないキーを参照した際にデフォルト値が入った状態になっている辞書型風のデータ構造です。

>>> import collections from defaultdict
>>> d = defaultdict(int)
>>> d[0]
0
>>> d = defaultdict(lambda:100)  # 100が返ってくるラムダ式
>>> d[1]
100

ちなみに標準の辞書型で同じことをすると例外が発生します。

>>> d = dict()
>>> d[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 0

一つのプログラムで状態を複数の状態を管理したいときには辞書型を良く使用するのですが、
今まで新しいキーが出現した際にはinや例外をキャッチしてデフォルト値を代入していました・・・。
長いだけならまだしもうっかりバグを発生させていたりしたのですが
defaultdictを使用することでシンプルかつバグりずらいコードが書けるようになりました。

deque

双方向から値の挿入、取り出しを行えうことができるスレッドセーフなキューです。

>>> import collections from deque
>>> d=deque([1,2,3,4])
>>> d.pop()
4
>>> d.popleft()
1
>>> d
deque([2, 3])
>>> d.appendleft(4)
>>> d.append(1)
>>> d
deque([4, 2, 3, 1])

別のスレッドに仕事を渡したいときは、Queueという標準ライブラリを使用する事が多いと思うのですが、
キューに割り込みの概念が欲しい時があり、そういう時に役に立ちました。

OrderedDict

値を追加した順序を記憶する事ができる辞書型風のデータ構造です。

>>> import collections from OrderedDict
>>> o=OrderedDict()
>>> o['first']=1
>>> o['second']=2
>>> o
OrderedDict([('first', 1), ('second', 2)])

とても便利そうで使用したいと良く思うものの、順序の情報は別のオブジェクトや辞書型のvalueに持たせることが多いため使用したことがありませんでした。
このデータ構造を用いるとfor文内で順序のカウントなどを行う必要がなくなるため関数型的に綺麗なプログラムが書けると思うので今後使用していきたいです。

namedtuple

tuple型の要素にクラスのメンバ風にアクセスできるようにするデータ構造です。

>>> N=namedtuple('Fruit',['apple','grape'])
>>> N(*[1,2])  # リストを展開
Fruit(apple=1, grape=2)
>>> n=N(*[1,2])
>>> n
Fruit(apple=1, grape=2)
>>> n.apple
1
>>> n.grape
2

使用するデータの構造が固定かつ値の読み出しが多い状況でコードの見やすさが向上するものの、
コードが短くなるわけでもないためあまり使用しない。

ChainMap

素早く複数の辞書を連結できるデータ構造です。

>>> c=ChainMap({1:2,3:4},{5:6,7:8})
>>> c=c.new_child({9:10,11:12})
>>> c
ChainMap({9: 10, 11: 12}, {1: 2, 3: 4}, {5: 6, 7: 8})
>>> c[9]
10
>>> c.maps
[{9: 10, 11: 12}, {1: 2, 3: 4}, {5: 6, 7: 8}]
>>> c.maps[0]
{9: 10, 11: 12}

連結した辞書を後から取り出せる所が特徴的だけどいまいち使い道が思い浮かばないデータ構造。
辞書型のupdateを多用するプログラムで高速化のために使用を検討する程度。