you can use dis(Disassembler for Python bytecode)
and check run-time
. best approach base run-time
and dis
is if 'key' not in my_dict: my_dict['key'] = 'new_value'
, because first and second approaches have CALL_METHOD
but third approach doesn't have CALL_METHOD
. (Benchmark on colab)
my_dict = {i:i for i in range(10_000_000)}
%timeit my_dict['key'] = my_dict.get('key', 'new_value')
# 1000000 loops, best of 5: 123 ns per loop
%timeit my_dict.setdefault('key', 'new_value')
# 10000000 loops, best of 5: 90.7 ns per loop
%timeit if 'key' not in my_dict: my_dict['key'] = 'new_value'
# 10000000 loops, best of 5: 51 ns per loop
Check with dis
>>> import dis
>>> my_dict = {}
>>> dis.dis("my_dict.setdefault('key', 'new_value')")
1 0 LOAD_NAME 0 (my_dict)
2 LOAD_METHOD 1 (setdefault)
4 LOAD_CONST 0 ('key')
6 LOAD_CONST 1 ('new_value')
8 CALL_METHOD 2
10 RETURN_VALUE
>>> dis.dis("my_dict['key'] = my_dict.get('key', 'new_value')")
1 0 LOAD_NAME 0 (my_dict)
2 LOAD_METHOD 1 (get)
4 LOAD_CONST 0 ('key')
6 LOAD_CONST 1 ('new_value')
8 CALL_METHOD 2
10 LOAD_NAME 0 (my_dict)
12 LOAD_CONST 0 ('key')
14 STORE_SUBSCR
16 LOAD_CONST 2 (None)
18 RETURN_VALUE
>>> dis.dis("if 'key' not in my_dict: my_dict['key'] = 'new_value'")
1 0 LOAD_CONST 0 ('key')
2 LOAD_NAME 0 (my_dict)
4 COMPARE_OP 7 (not in)
6 POP_JUMP_IF_FALSE 16
8 LOAD_CONST 1 ('new_value')
10 LOAD_NAME 0 (my_dict)
12 LOAD_CONST 0 ('key')
14 STORE_SUBSCR
>> 16 LOAD_CONST 2 (None)
18 RETURN_VALUE