38

のマイナーな煩わしさの 1 つdict.setdefaultは、最初の引数が既にディクショナリのキーである場合でも、常に 2 番目の引数を評価することです (もちろん、与えられた場合)。

例えば:

import random
def noisy_default():
    ret = random.randint(0, 10000000)
    print 'noisy_default: returning %d' % ret
    return ret

d = dict()
print d.setdefault(1, noisy_default())
print d.setdefault(1, noisy_default())

これにより、次のような出力が生成されます。

noisy_default: returning 4063267
4063267
noisy_default: returning 628989
4063267

最後の行で確認できるように、 の 2 回目の実行noisy_defaultは不要です。これは、この時点でキーが( with value )1に既に存在するためです。d4063267

2 番目の引数を遅延評価dictするメソッドのサブクラスを実装することは可能ですか?setdefault


編集:

以下は、BrenBarn のコメントと Pavel Anossov の回答に触発された実装です。その際、根底にある考え方は本質的に同じであるため、先に進んで get の遅延バージョンも実装しました。

class LazyDict(dict):
    def get(self, key, thunk=None):
        return (self[key] if key in self else
                thunk() if callable(thunk) else
                thunk)


    def setdefault(self, key, thunk=None):
        return (self[key] if key in self else
                dict.setdefault(self, key,
                                thunk() if callable(thunk) else
                                thunk))

さて、スニペット

d = LazyDict()
print d.setdefault(1, noisy_default)
print d.setdefault(1, noisy_default)

次のような出力が生成されます。

noisy_default: returning 5025427
5025427
5025427

上記の 2 番目の引数d.setdefaultは、関数呼び出しではなく呼び出し可能になっていることに注意してください。

LazyDict.getorの 2 番目の引数が呼び出し可能でない場合、対応するメソッドLazyDict.setdefaultと同じように動作します。dict

callable をデフォルト値自体として渡したい (つまり、呼び出されることを意図していない) 場合、または呼び出される callable に引数が必要な場合はlambda:、適切な引数を先頭に追加します。例えば:

d1.setdefault('div', lambda: div_callback)

d2.setdefault('foo', lambda: bar('frobozz'))

getandをオーバーライドするという考えが気に入らない場合setdefault、および/または結果として呼び出し可能性をテストする必要がある場合などは、代わりにこのバージョンを使用できます。

class LazyButHonestDict(dict):
    def lazyget(self, key, thunk=lambda: None):
        return self[key] if key in self else thunk()


    def lazysetdefault(self, key, thunk=lambda: None):
        return (self[key] if key in self else
                self.setdefault(key, thunk()))
4

4 に答える 4

24

これも で実現できますdefaultdict。これは、存在しない要素がアクセスされたときに呼び出される callable でインスタンス化されます。

from collections import defaultdict

d = defaultdict(noisy_default)
d[1] # noise
d[1] # no noise

の注意点defaultdictは、callable が引数を取得しないため、 のようにキーからデフォルト値を導出できないことですdict.setdefault__missing__これは、サブクラスでオーバーライドすることで軽減できます。

from collections import defaultdict

class defaultdict2(defaultdict):
    def __missing__(self, key):
        value = self.default_factory(key)
        self[key] = value
        return value

def noisy_default_with_key(key):
    print key
    return key + 1

d = defaultdict2(noisy_default_with_key)
d[1] # prints 1, sets 2, returns 2
d[1] # does not print anything, does not set anything, returns 2

詳細については、コレクションモジュールを参照してください。

于 2014-10-23T08:07:17.830 に答える
13

三項演算子を使用して、ワンライナーでそれを行うことができます。

value = cache[key] if key in cache else cache.setdefault(key, func(key))

が偽の値を格納しないことが確実な場合はcache、少し単純化できます。

value = cache.get(key) or cache.setdefault(key, func(key))
于 2016-02-05T10:16:42.403 に答える
11

いいえ、引数の評価は呼び出しの前に行われます。setdefaultcallable を 2 番目の引数として取り、必要な場合にのみ呼び出す、 のような関数を実装できます。

于 2013-07-08T17:53:19.017 に答える