How to create key or append an element to key?
Question:
I have an empty dictionary. Name: dict_x
It is to have keys of which values are lists.
From a separate iteration, I obtain a key (ex: key_123
), and an item (a tuple) to place in the list of dict_x
‘s value key_123
.
If this key already exists, I want to append this item.
If this key does not exist, I want to create it with an empty list and then append to it or just create it with a tuple in it.
In future when again this key comes up, since it exists, I want the value to be appended again.
My code consists of this:
Get key and value.
See if NOT key exists in dict_x
.
and if not create it: dict_x[key] == []
Afterwards: dict_x[key].append(value)
Is this the way to do it? Shall I try to use try/except
blocks?
Answers:
Use dict.setdefault()
:
dict.setdefault(key,[]).append(value)
help(dict.setdefault):
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
You can use defaultdict in collections
.
An example from doc:
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
You can use a defaultdict for this.
from collections import defaultdict
d = defaultdict(list)
d['key'].append('mykey')
This is slightly more efficient than setdefault
since you don’t end up creating new lists that you don’t end up using. Every call to setdefault
is going to create a new list, even if the item already exists in the dictionary.
Here are the various ways to do this so you can compare how it looks and choose what you like. I’ve ordered them in a way that I think is most “pythonic”, and commented the pros and cons that might not be obvious at first glance:
Using collections.defaultdict
:
import collections
dict_x = collections.defaultdict(list)
...
dict_x[key].append(value)
Pros: Probably best performance. Cons: Not available in Python 2.4.x.
Using dict().setdefault()
:
dict_x = {}
...
dict_x.setdefault(key, []).append(value)
Cons: Inefficient creation of unused list()
s.
Using try ... except
:
dict_x = {}
...
try:
values = dict_x[key]
except KeyError:
values = dict_x[key] = []
values.append(value)
Or:
try:
dict_x[key].append(value)
except KeyError:
dict_x[key] = [value]
dictionary['key'] = dictionary.get('key', []) + list_to_append
TL;DR
The more lengthy and expressive approach seems also to be more performant.
if key in dest:
dest[key].append(value)
else:
dest[key] = [value]
Longer answer
I wrote some python lines to check whether the proposed approach is actually the best in term of performance.
d1 = {}
d2 = {}
def add1(key, value, dest):
dest.setdefault(key, []).append(value)
def add2(key, value, dest):
if key in dest:
dest[key].append(value)
else:
dest[key] = [value]
This results in
%timeit add1('a', 1.1, d1)
96.2 ns ± 0.0972 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
%timeit add2('a', 1.1, d2)
89.1 ns ± 0.111 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
Tested on my python setup (3.10.4 (main, Apr 2 2022, 09:04:19) [GCC 11.2.0]
) executing Jupyter NB.
It’s worth it to point out that opting for the lengthier and more expressive approach can also impact positively on performance in this case.
I have an empty dictionary. Name: dict_x
It is to have keys of which values are lists.
From a separate iteration, I obtain a key (ex: key_123
), and an item (a tuple) to place in the list of dict_x
‘s value key_123
.
If this key already exists, I want to append this item.
If this key does not exist, I want to create it with an empty list and then append to it or just create it with a tuple in it.
In future when again this key comes up, since it exists, I want the value to be appended again.
My code consists of this:
Get key and value.
See if NOT key exists in
dict_x
.and if not create it:
dict_x[key] == []
Afterwards:
dict_x[key].append(value)
Is this the way to do it? Shall I try to use try/except
blocks?
Use dict.setdefault()
:
dict.setdefault(key,[]).append(value)
help(dict.setdefault):
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
You can use defaultdict in collections
.
An example from doc:
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
You can use a defaultdict for this.
from collections import defaultdict
d = defaultdict(list)
d['key'].append('mykey')
This is slightly more efficient than setdefault
since you don’t end up creating new lists that you don’t end up using. Every call to setdefault
is going to create a new list, even if the item already exists in the dictionary.
Here are the various ways to do this so you can compare how it looks and choose what you like. I’ve ordered them in a way that I think is most “pythonic”, and commented the pros and cons that might not be obvious at first glance:
Using collections.defaultdict
:
import collections
dict_x = collections.defaultdict(list)
...
dict_x[key].append(value)
Pros: Probably best performance. Cons: Not available in Python 2.4.x.
Using dict().setdefault()
:
dict_x = {}
...
dict_x.setdefault(key, []).append(value)
Cons: Inefficient creation of unused list()
s.
Using try ... except
:
dict_x = {}
...
try:
values = dict_x[key]
except KeyError:
values = dict_x[key] = []
values.append(value)
Or:
try:
dict_x[key].append(value)
except KeyError:
dict_x[key] = [value]
dictionary['key'] = dictionary.get('key', []) + list_to_append
TL;DR
The more lengthy and expressive approach seems also to be more performant.
if key in dest:
dest[key].append(value)
else:
dest[key] = [value]
Longer answer
I wrote some python lines to check whether the proposed approach is actually the best in term of performance.
d1 = {}
d2 = {}
def add1(key, value, dest):
dest.setdefault(key, []).append(value)
def add2(key, value, dest):
if key in dest:
dest[key].append(value)
else:
dest[key] = [value]
This results in
%timeit add1('a', 1.1, d1)
96.2 ns ± 0.0972 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
%timeit add2('a', 1.1, d2)
89.1 ns ± 0.111 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
Tested on my python setup (3.10.4 (main, Apr 2 2022, 09:04:19) [GCC 11.2.0]
) executing Jupyter NB.
It’s worth it to point out that opting for the lengthier and more expressive approach can also impact positively on performance in this case.