在我的一个类中,我有许多方法都从相同的字典中提取值。但是,如果其中一个方法尝试访问不存在的值,则它必须调用另一个方法以使该值与该键关联。
我目前已按以下方式实现此功能,其中findCrackDepth(tonnage)为self.lowCrackDepth [tonnage]分配一个值。
if tonnage not in self.lowCrackDepth: self.findCrackDepth(tonnage) lcrack = self.lowCrackDepth[tonnage]
但是,我也有可能这样做
try: lcrack = self.lowCrackDepth[tonnage] except KeyError: self.findCrackDepth(tonnage) lcrack = self.lowCrackDepth[tonnage]
我假设两者之间存在性能差异,这与值在字典中的频率有关。这个差异有多大?我正在生成数百万个这样的值(分布在该类的许多实例中的许多词典中),并且每次不存在该值时,它的值可能都会出现两次。
这是一个微妙的问题,因为您需要注意避免“持久的副作用”,并且性能折衷取决于丢失键的百分比。因此,请考虑以下dil.py文件:
dil.py
def make(percentmissing): global d d = dict.fromkeys(range(100-percentmissing), 1) def addit(d, k): d[k] = k def with_in(): dc = d.copy() for k in range(100): if k not in dc: addit(dc, k) lc = dc[k] def with_ex(): dc = d.copy() for k in range(100): try: lc = dc[k] except KeyError: addit(dc, k) lc = dc[k] def with_ge(): dc = d.copy() for k in range(100): lc = dc.get(k) if lc is None: addit(dc, k) lc = dc[k]
以及一系列timeit呼叫,例如:
timeit
$ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_in()' 10000 loops, best of 3: 28 usec per loop $ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_ex()' 10000 loops, best of 3: 41.7 usec per loop $ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_ge()' 10000 loops, best of 3: 46.6 usec per loop
这表明,缺少10%的密钥,in检查实际上是最快的方法。
in
$ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_in()' 10000 loops, best of 3: 24.6 usec per loop $ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_ex()' 10000 loops, best of 3: 23.4 usec per loop $ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_ge()' 10000 loops, best of 3: 42.7 usec per loop
仅丢失1%的密钥,该exception方法 略 快(get无论哪种情况,该方法仍然是最慢的一种)。
exception
get
因此,为了获得最佳性能,除非 绝大多数 (99%以上)的查找成功,否则该in方法是可取的。
当然,还有另一种优雅的可能性:添加像…这样的dict子类:
class dd(dict): def __init__(self, *a, **k): dict.__init__(self, *a, **k) def __missing__(self, k): addit(self, k) return self[k] def with_dd(): dc = dd(d) for k in range(100): lc = dc[k]
然而…:
$ python -mtimeit -s'import dil; dil.make(1)' 'dil.with_dd()' 10000 loops, best of 3: 46.1 usec per loop $ python -mtimeit -s'import dil; dil.make(10)' 'dil.with_dd()' 10000 loops, best of 3: 55 usec per loop
…虽然确实很漂亮,但这并不是性能上的赢家- 即便采用这种get方法,或更慢的速度,也要使用外观漂亮的代码来实现。(在defaultdict语义上类似于此类dd,如果适用的话,将是性能上的胜利,但这是因为__missing__在这种情况下,特殊方法是在经过优化的C代码中实现的)。
defaultdict
dd
__missing__