您的说法取决于输入,而不是绝对错误。
如果您有多样化的键集,并经常进入except
块,则性能不佳。如果try
块占主导地位,则try/except
惯用语在较小的列表上可以具有更高的性能。
这里是一个基准测试,展示了几种完成同一件事情的方式:
from __future__ import print_function
import timeit
import random
import collections
def f1():
d={}
for x in tgt:
if x in d:
d[x]+=1
else:
d[x]=1
return d
def f2():
d = {}
for x in tgt:
try:
d[x]+=1
except KeyError:
d[x] = 1
return d
def f3():
d={}.fromkeys(tgt, 0)
for x in tgt:
d[x]+=1
return d
def f4():
d=collections.defaultdict(int)
for x in tgt:
d[x]+=1
return d
def f5():
return collections.Counter(tgt)
def f6():
d={}
for x in tgt:
d[x]=d.setdefault(x, 0)+1
return d
def f7():
d={}
for x in tgt:
d[x]=d.get(x,0)+1
return d
def cmpthese(funcs, c=10000, rate=True, micro=False):
"""Generate a Perl style function benchmark"""
def pprint_table(table):
"""Perl style table output"""
def format_field(field, fmt='{:,.0f}'):
if type(field) is str: return field
if type(field) is tuple: return field[1].format(field[0])
return fmt.format(field)
def get_max_col_w(table, index):
return max([len(format_field(row[index])) for row in table])
col_paddings=[get_max_col_w(table, i) for i in range(len(table[0]))]
for i,row in enumerate(table):
row_tab=[row[0].ljust(col_paddings[0])]
row_tab+=[format_field(row[j]).rjust(col_paddings[j]) for j in range(1,len(row))]
print(' '.join(row_tab))
results={k.__name__:timeit.Timer(k).timeit(c) for k in funcs}
fastest=sorted(results,key=results.get, reverse=True)
table=[['']]
if rate: table[0].append('rate/sec')
if micro: table[0].append('usec/pass')
table[0].extend(fastest)
for e in fastest:
tmp=[e]
if rate:
tmp.append('{:,}'.format(int(round(float(c)/results[e]))))
if micro:
tmp.append('{:.3f}'.format(1000000*results[e]/float(c)))
for x in fastest:
if x==e: tmp.append('--')
else: tmp.append('{:.1%}'.format((results[x]-results[e])/results[e]))
table.append(tmp)
pprint_table(table)
if __name__=='__main__':
import sys
print(sys.version)
for j in [100,1000]:
for t in [(0,5), (0,50), (0,500)]:
tgt=[random.randint(*t) for i in range(j)]
print('{} rand ints between {}:'.format(j,t))
print('=====')
cmpthese([f1,f2,f3,f4,f5,f6,f7])
print()
我已经包含了一个基于timeit
的小型基准测试函数,它会按照从最慢到最快的顺序打印出函数,并显示它们之间的百分比差异。
以下是Python 3的结果:
3.4.1 (default, May 19 2014, 13:10:29)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
100 rand ints between (0, 5):
=====
rate/sec f6 f7 f1 f2 f3 f4 f5
f6 52,756 -- -1.6
f7 53,624 1.6
f1 71,491 35.5
f2 73,164 38.7
f3 76,148 44.3
f4 83,368 58.0
f5 99,247 88.1
100 rand ints between (0, 50):
=====
rate/sec f2 f6 f7 f4 f3 f1 f5
f2 39,405 -- -17.9
f6 47,980 21.8
f7 48,491 23.1
f4 48,737 23.7
f3 67,678 71.7
f1 75,511 91.6
f5 90,175 128.8
100 rand ints between (0, 500):
=====
rate/sec f2 f4 f6 f7 f3 f1 f5
f2 25,748 -- -22.0
f4 32,996 28.1
f6 43,930 70.6
f7 44,823 74.1
f3 60,624 135.5
f1 76,244 196.1
f5 80,026 210.8
1000 rand ints between (0, 5):
=====
rate/sec f7 f6 f1 f3 f2 f4 f5
f7 4,993 -- -6.7
f6 5,353 7.2
f1 7,640 53.0
f3 8,242 65.1
f2 8,982 79.9
f4 10,004 100.4
f5 17,293 246.4
1000 rand ints between (0, 50):
=====
rate/sec f7 f6 f1 f2 f3 f4 f5
f7 5,051 -- -7.1
f6 5,435 7.6
f1 6,873 36.1
f2 7,118 40.9
f3 7,661 51.7
f4 9,297 84.0
f5 17,531 247.1
1000 rand ints between (0, 500):
=====
rate/sec f2 f4 f6 f7 f3 f1 f5
f2 3,985 -- -11.0
f4 4,479 12.4
f6 4,613 15.8
f7 4,680 17.4
f3 5,361 34.5
f1 6,683 67.7
f5 12,028 201.8
还有Python 2:
2.7.6 (default, Dec 1 2013, 13:26:15)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)]
100 rand ints between (0, 5):
=====
rate/sec f5 f7 f6 f2 f1 f3 f4
f5 24,955 -- -41.8
f7 42,867 71.8
f6 43,382 73.8
f2 51,293 105.5
f1 56,357 125.8
f3 64,924 160.2
f4 71,709 187.3
100 rand ints between (0, 50):
=====
rate/sec f2 f5 f7 f6 f4 f3 f1
f2 22,439 -- -4.7
f5 23,553 5.0
f7 40,878 82.2
f6 41,164 83.4
f4 45,525 102.9
f3 61,167 172.6
f1 63,261 181.9
100 rand ints between (0, 500):
=====
rate/sec f2 f5 f4 f6 f7 f3 f1
f2 13,122 -- -39.9
f5 21,837 66.4
f4 29,945 128.2
f6 35,633 171.6
f7 36,257 176.3
f3 54,113 312.4
f1 65,570 399.7
1000 rand ints between (0, 5):
=====
rate/sec f5 f7 f6 f1 f2 f3 f4
f5 2,787 -- -37.7
f7 4,477 60.6
f6 4,524 62.3
f1 5,972 114.3
f2 6,953 149.5
f3 7,030 152.2
f4 8,452 203.3
1000 rand ints between (0, 50):
=====
rate/sec f5 f7 f6 f2 f1 f3 f4
f5 2,667 -- -37.8
f7 4,286 60.7
f6 4,351 63.1
f2 5,677 112.8
f1 6,045 126.6
f3 6,862 157.3
f4 7,687 188.2
1000 rand ints between (0, 500):
=====
rate/sec f2 f5 f7 f6 f4 f3 f1
f2 2,018 -- -16.1
f5 2,405 19.1
f7 3,609 78.8
f6 3,753 85.9
f4 4,334 114.7
f3 5,277 161.5
f1 5,454 170.2
所以,这取决于情况。
结论:
Counter方法几乎总是最慢的。
- 在Python 2上,Counter方法是最慢的之一,但在Python 3.4上则是最快的。
try/except
版本通常是最慢的。
if key in dict
版本无论大小或键数都可以预测地成为最好/最快的版本。
{}.fromkeys(tgt, 0)
非常可预测。
defaultdict
版本在较大的列表上速度最快。在较小的列表中,长时间的设置时间分摊在太少的元素上。
++
运算符 ;-) - Tim Peterscollections.Counter(l)
来完成此操作,或者对于Python版本低于2.7的情况下,可以使用collections.defaultdict(int)
。 - user2357112++
,所以如果您尝试++x
并发现它只是意味着+(+x)
,请不要感到惊讶。 - user2357112