代码之家  ›  专栏  ›  技术社区  ›  Wilduck

Python性能:是否在?

  •  3
  • Wilduck  · 技术社区  · 16 年前

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

    我目前的实现方式如下,findCrackDepth(tonety)为self.lowCrackDepth[tonety]赋值。

    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 回复  |  直到 16 年前
        1
  •  14
  •   Alex Martelli    16 年前

    计时是一个微妙的问题,因为您需要小心避免“持久的副作用”,性能权衡取决于丢失键的百分比。所以,考虑一下 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%+)的查找都会成功 最好采用这种方法。

    当然,还有另一种优雅的可能性:添加一个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
    

    ……虽然这确实很巧妙,但它并不是一个性能赢家——它与 得到 defaultdict ,语义上类似于 dd 类,如果它是适用的,将是一个性能的胜利,但这是因为 __missing__ 在这种情况下,特殊的方法是在优化良好的C代码中实现的。

        2
  •  3
  •   ThiefMaster    16 年前

    如果不在 更干净更易读的解决方案。

    根据您的问题,一个不存在的键不是一个类似于case的错误,因此没有很好的理由让python引发一个异常(即使您立即捕获它),如果您有一个 如果不在 检查,每个人都知道你的意图-获取现有值或以其他方式生成它。

        3
  •  2
  •   Oddthinking    16 年前

    如有疑问,请提供个人资料。

        4
  •  1
  •   Sjoerd    16 年前

    如果是例外情况,请使用例外情况。如果希望钥匙在里面,请使用try/except;如果不知道钥匙是否在里面,请使用 not in .

        5
  •  0
  •   Daenyth    16 年前

    我相信 .get() dict的方法有一个用于设置默认值的参数。你可以把它放在一行。不过,我不确定这对性能有何影响。