這不是不成熟的優化。我的使用案例在內部循環的最內部對dict的權利進行了雙重檢查,一直運行。此外,它在智力上令人厭煩(見結果)。Python字典查找性能,得到vs在
哪種方法更快?
mydict = { 'hello': 'yes', 'goodbye': 'no' }
key = 'hello'
# (A)
if key in mydict:
a = mydict[key]
do_things(a)
else:
handle_an_error()
# vs (B)
a = mydict.get(key,None)
if a is not None:
do_things(a)
else:
handle_an_error()
編輯:這些是相同的速度。常識告訴我,(B)應該明顯更快,因爲它只有一次字典查找與2次對比,但結果不同。我在撓頭。基準的
結果平均超過12運行,其中1/2都命中,而另一半是失誤:
doing in
switching to get
total time for IN: 0.532250006994
total time for GET: 0.480916659037
times found: 12000000
times not found: 12000000
當一個類似的運行(* 10個循環)而沒有找到關鍵,
doing in
switching to get
total time for IN: 2.35899998744
total time for GET: 4.13858334223
爲什麼!?
(正確的)代碼
import time
smalldict = {}
for i in range(10):
smalldict[str(i*4)] = str(i*18)
smalldict["8"] = "hello"
bigdict = {}
for i in range(10000):
bigdict[str(i*100)] = str(i*4123)
bigdict["hello"] = "yes!"
timetotal = 0
totalin = 0
totalget = 0
key = "hello"
found= 0
notfound = 0
ddo = bigdict # change to smalldict for small dict gets
print 'doing in'
for r in range(12):
start = time.time()
a = r % 2
for i in range(1000000):
if a == 0:
if str(key) in ddo:
found = found + 1
foo = ddo[str(key)]
else:
notfound = notfound + 1
foo = "nooo"
else:
if 'yo' in ddo:
found = found + 1
foo = ddo['yo']
else:
notfound = notfound + 1
foo = "nooo"
timetotal = timetotal + (time.time() - start)
totalin = timetotal/12.0
print 'switching to get'
timetotal = 0
for r in range(12):
start = time.time()
a = r % 2
for i in range(1000000):
if a == 0:
foo = ddo.get(key,None)
if foo is not None:
found = found + 1
else:
notfound = notfound + 1
foo = "nooo"
else:
foo = ddo.get('yo',None)
if foo is not None:
found = found + 1
notfound = notfound + 1
else:
notfound = notfound + 1
foo = "oooo"
timetotal = timetotal + (time.time() - start)
totalget = timetotal/12
print "total time for IN: ", totalin
print 'total time for GET: ', totalget
print 'times found:', found
print 'times not found:', notfound
(原始)碼 導入時間 smalldict = {} 對於i在範圍(10): smalldict [STR(I * 4)] = STR(ⅰ * 18)
smalldict["8"] = "hello"
bigdict = {}
for i in range(10000):
bigdict[str(i*100)] = str(i*4123)
bigdict["8000"] = "hello"
timetotal = 0
totalin = 0
totalget = 0
key = "hello"
found= 0
notfound = 0
ddo = bigdict # change to smalldict for small dict gets
print 'doing in'
for r in range(12):
start = time.time()
a = r % 2
for i in range(10000000):
if a == 0:
if key in ddo:
foo = ddo[key]
else:
foo = "nooo"
else:
if 'yo' in ddo:
foo = ddo['yo']
else:
foo = "nooo"
timetotal = timetotal + (time.time() - start)
totalin = timetotal/12.0
print 'switching to get'
timetotal = 0
for r in range(12):
start = time.time()
a = r % 2
for i in range(10000000):
if a == 0:
foo = ddo.get(key,None)
if foo is not None:
# yaaay
pass
else:
foo = "nooo"
else:
foo = ddo.get('yo',None)
if foo is not None:
#yaaay
pass
else:
foo = "oooo"
timetotal = timetotal + (time.time() - start)
totalget = timetotal/12
print "total time for IN: ", totalin
print 'total time for GET: ', totalget
它看起來並不像你實際上計時的情況,其中的關鍵是在字典中,所以'in'代碼實際上從來沒有實現雙重查找。 – user2357112
@ user2357112:很好的觀察。我猜想,成員函數查找爲'get'做了其餘的工作。我通過預先查看'get':'g = ddo.get'將時間減少了10%,然後調用'foo = g('yo',None)' –
查找屬性並調用必須返回對象的方法*或*默認值永遠不會像單個操作碼那麼快,只會檢查密鑰的存在。儘管使用'timeit'模塊來做適當的時間試驗。 –