dict.setdefault
的一個小問題是它總是評估它的第二個參數(當然給出的時候),即使第一個參數已經是字典中的一個鍵。如何實現一個懶惰的setdefault?
例如:
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())
這產生ouptut類似如下:
noisy_default: returning 4063267
4063267
noisy_default: returning 628989
4063267
作爲最後行可確認,的noisy_default
第二執行是不必要的,因爲通過這一點的關鍵1
是已經存在於d
(值爲4063267
)。
是否有可能實現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
注意,上述的第二個參數是d.setdefault
現在一個可調用的,而不是函數調用。
當LazyDict.get
或LazyDict.setdefault
的第二個參數不可調用時,它們的行爲方式與對應的dict
方法相同。
如果一個人想通過一個可調用作爲默認值本身(即,不意味着被調用),或者如果可調用被調用需要參數,前置lambda:
到相應的參數。例如:
d1.setdefault('div', lambda: div_callback)
d2.setdefault('foo', lambda: bar('frobozz'))
那些誰不喜歡重寫get
和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()))
你不能讓它不評估第二個參數。你必須做的是將該參數封裝在一個函數中(例如,用'lambda'),然後讓'setdefault'只在需要時調用該函數。 – BrenBarn
我可以建議你將'* args,** kwargs'添加到'lazyget',lazysetdefault'和對'thunk()'的調用中嗎?這將允許你的懶惰的東西參數。例如'lbd.lazysetdefault('total',sum,[1,2,3,4],start = 2)' – Hounshell