次のコードを機能させるためにdefaultdict(defaultdict(int))
を使用する方法はありますか?
for x in stuff:
d[x.a][x.b] += x.c_int
d
は、x.a
およびx.b
要素に応じて、アドホックに構築する必要があります。
私は使用できます:
for x in stuff:
d[x.a,x.b] += x.c_int
しかし、私は使用できません:
d.keys()
d[x.a].keys()
はい、このように:
defaultdict(lambda: defaultdict(int))
存在しないキーにアクセスしようとすると、defaultdict
(この場合はlambda: defaultdict(int)
)の引数が呼び出されます。戻り値はこのキーの新しい値として設定されます。つまり、この場合、d[Key_doesnt_exist]
の値はdefaultdict(int)
になります。
この最後のdefaultdictからキーにアクセスしようとすると、つまりd[Key_doesnt_exist][Key_doesnt_exist]
は、0を返します。これは、最後のdefaultdictの引数の戻り値、つまりint()
です。
Defaultdictコンストラクターのパラメーターは、新しい要素を構築するために呼び出される関数です。ラムダを使用しましょう!
>>> from collections import defaultdict
>>> d = defaultdict(lambda : defaultdict(int))
>>> print d[0]
defaultdict(<type 'int'>, {})
>>> print d[0]["x"]
0
Python 2.7以降、 Counterを使用したさらに優れたソリューション があります。
>>> from collections import Counter
>>> c = Counter()
>>> c["goodbye"]+=1
>>> c["and thank you"]=42
>>> c["for the fish"]-=5
>>> c
Counter({'and thank you': 42, 'goodbye': 1, 'for the fish': -5})
いくつかのボーナス機能
>>> c.most_common()[:2]
[('and thank you', 42), ('goodbye', 1)]
詳細については、「 PyMOTW-コレクション-コンテナデータタイプ 」および「 Pythonドキュメント-コレクション 」を参照してください。
partial
を使用すると、少しエレガントになります。
import functools
dd_int = functools.partial(defaultdict, int)
defaultdict(dd_int)
もちろん、これはラムダと同じです。
参考のために、次の方法で汎用のネストされたdefaultdict
ファクトリーメソッドを実装できます。
from collections import defaultdict
from functools import partial
from itertools import repeat
def nested_defaultdict(default_factory, depth=1):
result = partial(defaultdict, default_factory)
for _ in repeat(None, depth - 1):
result = partial(defaultdict, result)
return result()
深さは、default_factory
で定義されたタイプが使用される前のネストされた辞書の数を定義します。例えば:
my_dict = nested_defaultdict(list, 3)
my_dict['a']['b']['c'].append('e')
他の人は、以下を機能させる方法についてのあなたの質問に正しく答えています。
for x in stuff:
d[x.a][x.b] += x.c_int
別の方法は、キーにタプルを使用することです。
d = defaultdict(int)
for x in stuff:
d[x.a,x.b] += x.c_int
# ^^^^^^^ Tuple key
このアプローチの良いところは、シンプルで簡単に拡張できることです。 3レベルの深さのマッピングが必要な場合は、キーに3項目のタプルを使用します。