Python性能:尝试 - 不在或不在?

时间:2010-06-24 15:03:23

标签: python performance

在我的一个类中,我有许多方法都从相同的字典中绘制值。但是,如果其中一个方法试图访问不存在的值,则必须调用另一个方法来使该值与该键相关联。

我目前实现如下,其中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]

我假设两者之间存在性能差异,这与表中词的频率有关。这个差异有多大?我正在生成几百万个这样的值(在该类的许多实例中分布在许多字典中),并且每次该值都不存在时,可能会有两次这样的值。

5 个答案:

答案 0 :(得分:14)

这是一个微妙的问题,因为你需要注意避免“持久的副作用”,性能权衡取决于缺失键的百分比。因此,请考虑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来电,例如:

$ 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检查基本上是最快的方式。

$ 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方法在任何一种情况下仍然是最慢的方法。)

因此,为了获得最佳性能,除非浩大多数(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中实现的码)。

答案 1 :(得分:3)

检查密钥是否存在更便宜或至少与检索密钥一样便宜。因此,如果不在解决方案中,请使用,它更清晰,更易读。

根据你的问题,一个不存在的密钥不是一个类似错误的情况,因此没有充分的理由让python引发异常(即使你立即捕获它),如果你有一个,如果没有/ em>检查,每个人都知道你的意图 - 获得现有价值或以其他方式生成它。

答案 2 :(得分:2)

如有疑问,请参阅。

运行测试以查看在您的环境中,运行速度是否比另一个运行得快。

答案 3 :(得分:1)

如果是例外,请使用例外。如果您希望密钥在那里,请使用try / except,如果您不知道密钥是否在那里,请使用not in

答案 4 :(得分:0)

我相信dict的.get()方法有一个用于设置默认值的参数。您可以使用它并将其放在一行中。我不确定它会如何影响性能。