web-dev-qa-db-ja.com

キーをdefaultdictのdefault_factoryに渡す賢い方法はありますか?

クラスには、1つのパラメーターを取るコンストラクターがあります。

class C(object):
    def __init__(self, v):
        self.v = v
        ...

コードのどこかで、dictの値がキーを知っていると便利です。
新生児のデフォルト値に渡されるキーでdefaultdictを使用したい:

d = defaultdict(lambda : C(here_i_wish_the_key_to_be))

助言がありますか?

84

cleverとはほとんど見なされませんが、サブクラス化はあなたの友人です:

class keydefaultdict(defaultdict):
    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError( key )
        else:
            ret = self[key] = self.default_factory(key)
            return ret

d = keydefaultdict(C)
d[x] # returns C(x)
117
Jochen Ritzel

いいえ、ありません。

defaultdict実装は、不足しているkeyをそのままdefault_factoryに渡すように構成できません。上記の@JochenRitzelで提案されているように、あなたの唯一の選択肢はあなた自身のdefaultdictサブクラスを実装することです。

しかし、それは「賢い」ものではなく、または標準のライブラリソリューション(存在する場合)ほどクリーンではありません。 このようにあなたの簡潔への答え、はい/いいえの質問は明らかに「いいえ」です。

標準ライブラリにそのような頻繁に必要なツールが欠けているのは残念です。

25
Stuart Berg

ここではdefaultdictは必要ないと思います。なぜdict.setdefault 方法?

>>> d = {}
>>> d.setdefault('p', C('p')).v
'p'

もちろん、それによってCのインスタンスが多数作成されます。それが問題である場合、私はより簡単なアプローチでうまくいくと思います:

>>> d = {}
>>> if 'e' not in d: d['e'] = C('e')

私の知る限り、defaultdictや他の代替手段よりも高速です。

[〜#〜] eta [〜#〜]inテストの速度と、try-except句の使用との関係について:

>>> def g():
    d = {}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(g)
0.19638929363557622
>>> def f():
    d = {}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(f)
0.6167065411074759
>>> def k():
    d = {'a': 2}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(k)
0.30074866358404506
>>> def p():
    d = {'a': 2}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(p)
0.28588609450770264
6
SilentGhost