Pythonの組み込みdictでは存在しないキーで値を取り出そうとするとKeyErrorが発生するので、例えばキーで指定した値をインクリメントする処理なんかは、以下のようなお決まりのパターンで書くことが多いです。
d = {} if k in d: d[k] += 1 else: d[k] = 1
でも、1行で書きたければ以下のように書けます。
d = {} d[k] = d.get(k, 0) + 1
defaultdict(v2.5から標準モジュールになってる)を使うと次のようにも書けます。
(ちなみにgetを使う方法よりdefaultdictを使うほうが速いそうです)
from collections import defaultdict d = defaultdict(int) d[k] += 1
defaultdictに渡す引数はデフォルト値を返すcallable objectです。
(int()を実行するとintのデフォルト値である0が得られる)
オフィシャルのサンプルにも上記のようにdefaultdictにintを渡す書き方が載ってますが、個人的に微妙な感じがする(知らない人が見たら辞書の値をint型に限定してるのか?と勘違いするかもしれない)のでちょっと変えて、以下のように書いてます。
from collections import defaultdict d = defaultdict(lambda: 0) d[k] += 1
これだとデフォルト値を1とかもっと他の数字に変えたいときや、もっと奇妙な文字列にしたいときも同じ書き方で対応できるのでより一般的かと思います。
ちなみにdefaultdictでは、存在しないキーを指定してデフォルト値を返す際にそのキーとデフォルト値で新たにデータが追加されます。
これが文脈によっては好ましくない副作用となる場合がありますのでご注意を。下記参照。
(キーが存在するかしないかだけチェックできればいいのに無駄にメモリを食うとかキーが存在しないという情報を失うとか)
# defaultdictのマズイ使い方 from collections import defaultdict dd = defaultdict(lambda: '*', {'a': 'aa', 'b': 'bb', 'c': 'cc'}) print dd # defaultdict(<function <lambda> at 0x100510668>, {'a': 'aa', 'c': 'cc', 'b': 'bb'}) for code in xrange(ord('a'), ord('z') + 1): if dd[chr(code)] != "*": pass # 何らかの処理 print dd # defaultdict(<function <lambda> at 0x100510668>, {'a': 'aa', 'c': 'cc', 'b': 'bb', 'e': '*', 'd': '*', 'g': '*', 'f': '*', 'i': '*', 'h': '*', 'k': '*', 'j': '*', 'm': '*', 'l': '*', 'o': '*', 'n': '*', 'q': '*', 'p': '*', 's': '*', 'r': '*', 'u': '*', 't': '*', 'w': '*', 'v': '*', 'y': '*', 'x': '*', 'z': '*'})
# inでキーの存在チェックをすると回避できる from collections import defaultdict dd = defaultdict(lambda: '*', {'a': 'aa', 'b': 'bb', 'c': 'cc'}) print dd # defaultdict(<function <lambda> at 0x100510668>, {'a': 'aa', 'c': 'cc', 'b': 'bb'}) for code in xrange(ord('a'), ord('z') + 1): if chr(code) in dd: pass # 何らかの処理 print dd # defaultdict(<function <lambda> at 0x100510668>, {'a': 'aa', 'c': 'cc', 'b': 'bb'})
コメント