增加字典中计数器的最快方法
Fastest way to increment counters in dictionary
递增存储在字典中的计数器的最快方法是什么?
因为我必须执行相同的操作数十万次,所以我正在寻找比下面的方法更有效的方法:
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
在您的情况下,您可以只使用布尔值(比较的结果)可以简单地转换为整数这一事实。它可能不是最快的,但它绝对短而且 "relatively" 快:
def funcA(a):
adict = {key: int(key <= a) for key in range(1, 51)}
这是假设第二个函数实际上是您想要的,因为第一个函数可能会因为 break
而给出不同的结果。字典是无序的,因此它不能为小于或等于 a
的键增加一些值。它也不会增加值,它只是将它们设置为 1
或 0
因为在这种情况下你实际上不需要添加。
但是,这不一定是最快的方法,因为它必须执行大量函数调用和 int
查找。因此,我将按性能顺序(从最快到最慢)展示一些更等效的操作:
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)
.
,性能可能会完全不同
递增存储在字典中的计数器的最快方法是什么?
因为我必须执行相同的操作数十万次,所以我正在寻找比下面的方法更有效的方法:
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
在您的情况下,您可以只使用布尔值(比较的结果)可以简单地转换为整数这一事实。它可能不是最快的,但它绝对短而且 "relatively" 快:
def funcA(a):
adict = {key: int(key <= a) for key in range(1, 51)}
这是假设第二个函数实际上是您想要的,因为第一个函数可能会因为 break
而给出不同的结果。字典是无序的,因此它不能为小于或等于 a
的键增加一些值。它也不会增加值,它只是将它们设置为 1
或 0
因为在这种情况下你实际上不需要添加。
但是,这不一定是最快的方法,因为它必须执行大量函数调用和 int
查找。因此,我将按性能顺序(从最快到最慢)展示一些更等效的操作:
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)
.