While running a numerical integrator, I noticed a noticeable difference in speed depending on how I extract the value of the field in a dictionary
import num
Python has to do more work for dict.get()
:
get
is an attribute, so Python has to look this up, and then bind the descriptor found to the dictionary instance.()
is a call, so the current frame has to be pushed on the stack, a call has to be made, then the frame has to be popped again from the stack to continue.The [...]
notation, used with a dict
, doesn't require a separate attribute step or frame push and pop.
You can see the difference when you use the Python bytecode disassembler dis:
>>> import dis
>>> dis.dis(compile('d[key]', '', 'eval'))
1 0 LOAD_NAME 0 (d)
3 LOAD_NAME 1 (key)
6 BINARY_SUBSCR
7 RETURN_VALUE
>>> dis.dis(compile('d.get(key)', '', 'eval'))
1 0 LOAD_NAME 0 (d)
3 LOAD_ATTR 1 (get)
6 LOAD_NAME 2 (key)
9 CALL_FUNCTION 1
12 RETURN_VALUE
so the d[key]
expression only has to execute a BINARY_SUBSCR
opcode, while d.get(key)
adds a LOAD_ATTR
opcode. CALL_FUNCTION
is a lot more expensive than BINARY_SUBSCR
on a built-in type (custom types with __getitem__
methods still end up doing a function call).
If the majority of your keys exist in the dictionary, you could use try...except KeyError
to handle missing keys:
try:
return mydict['name']
except KeyError:
return None
Exception handling is cheap if there are no exceptions.