一个小烦恼dict.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())
这会产生如下输出:
noisy_default: returning 4063267
4063267
noisy_default: returning 628989
4063267
正如最后一行所证实的,第二次执行noisy_default
是不必要的,因为此时键1
已经存在于d
(带有 value 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
or的第二个参数LazyDict.setdefault
不是可调用的时,它们的行为方式与相应的dict
方法相同。
如果一个人想要传递一个可调用对象作为默认值本身(即,不意味着被调用),或者如果要调用的可调用对象需要参数,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()))