I was just looking at the Python FAQ because it was mentioned in another question. Having never really looked at it in detail before, I came across this question: “How fast
If the case where the key is not found is more than exceptional, I would suggest using the 'get' method, which provide a constant speed in all cases :
s.append('''\
x = D.get('key', None)
''')
s.append('''\
x = D.get('xxx', None)
''')
Catching exceptions is expensive, but exceptions should be exceptional (read, not happen very often). If exceptions are rare, try/catch is faster than LBYL.
The following example times a dictionary key lookup using exceptions and LBYL when the key exists and when it doesn't exist:
import timeit
s = []
s.append('''\
try:
x = D['key']
except KeyError:
x = None
''')
s.append('''\
x = D['key'] if 'key' in D else None
''')
s.append('''\
try:
x = D['xxx']
except KeyError:
x = None
''')
s.append('''\
x = D['xxx'] if 'xxx' in D else None
''')
for i,c in enumerate(s,1):
t = timeit.Timer(c,"D={'key':'value'}")
print('Run',i,'=',min(t.repeat()))
Run 1 = 0.05600167960596991 # try/catch, key exists
Run 2 = 0.08530091918578364 # LBYL, key exists (slower)
Run 3 = 0.3486251291120652 # try/catch, key doesn't exist (MUCH slower)
Run 4 = 0.050621117060586585 # LBYL, key doesn't exist
When the usual case is no exception, try/catch is "extremely efficient" when compared to LBYL.
The cost depends on implementation, obviously, but I wouldn't worry about it. It's unlikely going to matter, anyway. Standard protocols raise exceptions in strangest of places (think StopIteration), so you're surrounded with raising and catching whether you like it or not.
When choosing between LBYL and EAFP, worry about readability of the code, instead of focusing on micro-optimisations. I'd avoid type-checking if possible, as it might reduce the generality of the code.