2017-08-31 47 views
3

增加字典中存储的计数器的最快方法是什么?增加字典中计数器的最快方法

因为我必须这样做同样的操作几百次几千,我正在寻找的东西比我有什么下面更高效:

def funcA(a): 
    keys = [x for x in range(1, 51)] 
    adict = {key: 0 for key in keys} 

    for k in adict.keys(): # this is the code I would like to improve 
     if k <= a: 
      adict[k] += 1 
     else: 
      break 


import timeit 
number = 100000 

t1 = timeit.timeit(
     'funcA(5)', 
     setup="from __main__ import funcA", number=number) 
print(t1) 

>>> 0.42629639082588255 

试图用一个列表理解,而不是似乎将放缓所有的一切,也许是因为它缺少break声明?

def funcB(a): 
    # not working, invalid syntax 
    keys = [x for x in range(1, 51)] 
    adict = {key: 0 for key in keys} 

    def _inc(x): 
     x += 1 
     return x 

    [_inc(adict[k]) for k in adict.keys() if k <= a] 

# Timing: 0.5831785711925477 

注:最初我有if float(k) <= float(a):但因为我只希望号(整数或浮点数),去除float()转换提高了代码。这个假设是否合理?

注2:在一些评论所指出的,break语句可以产生意外的结果所产生的字典,所以不如只是做:

def funcA(a): 
    keys = [x for x in range(1, 51)] 
    adict = {key: 0 for key in keys} 

    for k in adict: 
     if k <= a: 
      adict[k] += 1 

# Timing: 0.5132114209700376 
+1

为什么不这样做:'adict = {key:1如果key <= a else 0 for keys in keys}',那么可以将adict.keys()中的k缩短为'for for k in adict' –

+2

? –

+0

@AshwiniChaudhary我想增加关键词'adict [k] + = 1' – PedroA

回答

4

你的情况,你可以只使用一个事实,即布尔(比较结果)可以简单地转换为整数。它可能不是最快的,但它绝对是短,“相对”快:

def funcA(a): 
    adict = {key: int(key <= a) for key in range(1, 51)} 

这是假设第二个函数实际上是你想要的,因为第一个可以给因为break不同的结果。字典是无序的,因此它不能为小于或等于a的键增加一些值。它也不增加值,它只是将它们设置为10,因为在这种情况下实际上不需要添加。

但是,这不一定是最快的方法,因为它必须执行大量的函数调用和查找。所以,我会为了呈现一些等同操作的性能(最快到最慢):

def cached_version(): 
    range_cache = range(1, 51) 
    cache = dict.fromkeys(range_cache, 0) 
    def inner(a): 
     adict = cache.copy() 
     for key in range_cache[:a]: # requires a to be an integer! 
      adict[key] = 1 
     return adict 
    return inner 

func1 = cached_version() # initialize cache 

def func2(a): 
    keys = range(1, 51) 
    adict = dict.fromkeys(keys[:a], 1) # requires a to be an integer! 
    for key in keys[a:]: 
     adict[key] = 0 
    return adict 

def func3(a): 
    adict = {} 
    for key in range(1, 51): 
     if key <= a: 
      adict[key] = 1 
     else: 
      adict[key] = 0 
    return adict 

def func4(a): 
    return {key: 1 if key <= a else 0 for key in range(1, 51)} 

def func5(a): 
    keys = range(1, 51) 
    adict = dict.fromkeys(keys[:a], 1) # requires a to be an integer! 
    adict.update(dict.fromkeys(keys[a:], 0)) 
    return adict 

def func6(a): 
    return dict(zip(range(1, 51), [1]*a + [0]*(49-a))) # requires a to be an integer! 

from itertools import chain 

def func7(a): 
    return dict(zip(range(1, 51), chain([1]*a, [0]*(49-a)))) # requires a to be an integer! 

def func8(a): # the one I originally mentioned 
    adict = {key: int(key <= a) for key in range(1, 51)} 

的时序是关于Python 3.5的Windows 10做,有可能是在其他机器和其他Python版本的差异。另请注意,如果您拥有更多密钥而不是range(1, 51),则性能可能会完全不同。

+0

的确如此,但这个解决方案似乎比我所拥有的任何一个功能都要慢,即使没有“break”。你说得对,'break'声明可能会混淆结果。 – PedroA

+0

@PedroA好吧,我做了一些时间,并尝试了各种组合。我想我找到了一个相当快的(虽然它是一个关闭缓存)。 :) – MSeifert

+0

不错,但我很惊讶'func3'比'func4'快,认为comphrension会更快? –