のマイナーな煩わしさの 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
に既に存在するためです。d
4063267
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.get
orの 2 番目の引数が呼び出し可能でない場合、対応するメソッドLazyDict.setdefault
と同じように動作します。dict
callable をデフォルト値自体として渡したい (つまり、呼び出されることを意図していない) 場合、または呼び出される callable に引数が必要な場合はlambda:
、適切な引数を先頭に追加します。例えば:
d1.setdefault('div', lambda: div_callback)
d2.setdefault('foo', lambda: bar('frobozz'))
get
andをオーバーライドするという考えが気に入らない場合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()))