0

Suppose that I want to add a value to a dict, but I'm not sure if the key exists. What would be the better way (performance matter)? If there is some better way...

my_dict['key'] = my_dict.get('key', new_value) # I like this!

OR

if 'key' not in my_dict:
    my_dict['key'] = new_value
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437

2 Answers2

4

There's a built-in dict method for this:

my_dict.setdefault('key', new_value)

It returns the resulting value (i.e. the existing value if there was one, or the newly set one if there wasn't), which is useful in expressions like:

my_dict.setdefault('key', []).append('foo')
Samwise
  • 68,105
  • 3
  • 30
  • 44
1

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
I'mahdi
  • 23,382
  • 5
  • 22
  • 30
  • 1
    Really impressive. *Seeing* is *believing*.... Do you know what's *performance* implications for each - if that's handy. Thanks – Daniel Hao Jul 07 '22 at 14:40