python – 在collections.defaultdict中禁止键添加
当在defaultdict对象中查询缺少的键时,该键会自动添加到字典中:
from collections import defaultdict d = defaultdict(int) res = d[5] print(d) # defaultdict(<class 'int'>,{5: 0}) # we want this dictionary to remain empty 但是,我们通常只想在显式或隐式分配密钥时添加密钥: d[8] = 1 # we want this key added d[3] += 1 # we want this key added 一个用例是简单计数,以避免集合的较高开销.计数器,但这个功能一般也是可取的. 反例[原谅双关语] 这是我想要的功能: from collections import Counter c = Counter() res = c[5] # 0 print(c) # Counter() c[8] = 1 # key added successfully c[3] += 1 # key added successfully 但Counter比defaultdict(int)明显慢.我发现性能下降通常比defaultdict(int)慢约2倍. 另外,显然Counter只能与defaultdict中的int参数相媲美,而defaultdict可以采用list,set等. 有没有办法有效地实现上述行为;例如,通过继承defaultdict? 基准测试示例 %timeit DwD(lst) # 72 ms %timeit dd(lst) # 44 ms %timeit counter_func(lst) # 98 ms %timeit af(lst) # 72 ms 测试代码: import numpy as np from collections import defaultdict,Counter,UserDict class DefaultDict(defaultdict): def get_and_forget(self,key): _sentinel = object() value = self.get(key,_sentinel) if value is _sentinel: return self.default_factory() return value class DictWithDefaults(dict): __slots__ = ['_factory'] # avoid using extra memory def __init__(self,factory,*args,**kwargs): self._factory = factory super().__init__(*args,**kwargs) def __missing__(self,key): return self._factory() lst = np.random.randint(0,10,100000) def DwD(lst): d = DictWithDefaults(int) for i in lst: d[i] += 1 return d def dd(lst): d = defaultdict(int) for i in lst: d[i] += 1 return d def counter_func(lst): d = Counter() for i in lst: d[i] += 1 return d def af(lst): d = DefaultDict(int) for i in lst: d[i] += 1 return d 关于赏金评论的注意事项: 自Bounty提供以来,@Aran-Fey’s solution已经更新,所以请忽略Bounty的评论. 解决方法
而不是搞乱
collections.defaultdict 使它做我们想要的,似乎更容易实现我们自己:
class DefaultDict(dict): def __init__(self,default_factory,**kwargs): super().__init__(**kwargs) self.default_factory = default_factory def __getitem__(self,key): try: return super().__getitem__(key) except KeyError: return self.default_factory() 这可以按照您想要的方式工作: d = DefaultDict(int) res = d[5] d[8] = 1 d[3] += 1 print(d) # {8: 1,3: 1} 但是,对于可变类型,它可能会出乎意料地表现: d = DefaultDict(list) d[5].append('foobar') print(d) # output: {} 这可能是defaultdict在访问不存在的密钥时记住该值的原因. 另一种选择是扩展defaultdict并添加一个新方法来查找值而不记住它: from collections import defaultdict class DefaultDict(defaultdict): def get_and_forget(self,key): return self.get(key,self.default_factory()) 请注意,无论密钥是否已存在于dict中,get_and_forget方法每次都会调用default_factory().如果这是不合需要的,您可以使用sentinel值来实现它: class DefaultDict(defaultdict): def get_and_forget(self,_sentinel) if value is _sentinel: return self.default_factory() return value 这样可以更好地支持可变类型,因为它允许您选择是否应该将值添加到dict中. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |