Indeed, your structure is a list of dictionaries.
>>> lst = [{'a': 5}, {'b': 6}, {'c': 7}, {'d': 8}]
To get a better idea of what is happening with your first condition, try this:
>>> gen = (itm for itm in lst if itm['a'] == 5)
>>> next(gen)
{'a': 5}
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
KeyError: 'a'
Each time you call next
, you process the next element and return an item. Also...
next((itm for itm in lst if itm['a'] == 5))
Creates a generator that is not assigned to any variable, processes the first element in the lst
, sees that key 'a'
does indeed exist, and return the item. The generator is then garbage collected. The reason an error is not thrown is because the first item in lst
does indeed contain this key.
So, if you changed the key to be something that the first item does not contain, you get the error you saw:
>>> gen = (itm for itm in lst if itm['b'] == 6)
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
KeyError: 'b'
The Solution
Well, one solution as already discussed is to use the dict.get
function. Here's another alternative using defaultdict
:
from collections import defaultdict
from functools import partial
f = partial(defaultdict, lambda: None)
lst = [{'a': 5}, {'b': 6}, {'c': 7}, {'d': 8}]
lst = [f(itm) for itm in lst] # create a list of default dicts
for i in (itm for itm in lst if itm['b'] == 6):
print(i)
This prints out:
defaultdict(<function <lambda> at 0x10231ebf8>, {'b': 6})
The defaultdict
will return None
in the event of the key not being present.