Printing from [Awkward] Python Nested Dictionaries
Question:
So, I’m learning Python (3.x) and it makes a lot of sense until I came across this very awkward progression of dictionaries and trying to get a better understanding of what it is doing. I’m hoping someone could explain in [simple] terms why it does/doesn’t work.
I’m fully aware that there may be better ways of doing what I am trying to do – so I’m open to seeing and learning those too!
I need to extract/print ‘this_entry’ in these examples.
dict1 = {'simple':'this_entry'}
dict2 = {'key1':{'key2':'this_entry'}}
dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
The first two work absolutely fine using
print (dict1['simple'])
print (dict2['key1']['key2'])
BUT the third one is (obviously designed to be) much more tricky, if I use
print (dict3['key1']['nest_key'][1])
It doesn’t work and throws up errors…
So to debug I started with the base level to see what works/what doesn’t.
print (dict3['key1']) #shows [{'nest_key': ['this is harder', ['hello']]}]
print (dict3['key1']['nest_key'][1]) #breaks here showing:
#TypeError: list indices must be integers or slices, not str
Why does it break there? If I add in a ‘pointer’ for the key (is that even the right word?) it kinda works, I just want to know why it breaks if that isn’t used and why the ‘pointer’ is needed in this instance (other than ‘it just doesn’t work without it’).
print (dict3['key1'][0]['nest_key'][1]) #works-ish but shows entry ['hello'], not hello
There’s something obviously missing too as the output is incorrect. I’m guessing understaning why I need the first [0] will also explain on how to extract the entry I desire properly?
Thanks in advance!
Answers:
With your given dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
, consider running the following statements, and the output they give:
>>> print(dict3["key1"]) # access the value of 'key1', the value is a list
[{'nest_key': ['this is harder', ['this_entry']]}]
>>> print(dict3["key1"][0]) # access the 1st elemtn of the list which is a dict
{'nest_key': ['this is harder', ['this_entry']]}
>>> print(dict3["key1"][0]["nest_key"]) # access the value of 'nest_key, the value is a list
['this is harder', ['this_entry']]
>>> print(dict3["key1"][0]["nest_key"][1]) # get 2nd element of the list, which is another list
['this_entry']
>>> print(dict3["key1"][0]["nest_key"][1][0]) # finaly get the single item in the list, value is a string
this_entry
TL;DR
dict3['key1']
-> returns a list
- i.e.
[{'nest_key': ['this is harder']}]
dict3['key1'][0]
-> returns first item in the list
- i.e.
{'nest_key': ['this is harder']}
dict3['key1'][0]['nest_key']
-> returns the nest_key value in the inner dict
- i.e.
['this is harder']
dict3['key1'][0]['nest_key'][0]
-> returns the first item in the inner dict
- i.e.
'this is harder'
dict3['key1'][0]['nest_key'][1]
-> returns the 2nd item in the inner dict
- i.e.
['this_entry']
dict3['key1'][0]['nest_key'][1][0]
-> returns the 1st string in the 2nd item in the inner dict
- i.e.
'this_entry'
dict3['key1'][0]['nest_key'][1][0][0]
-> returns the 1st character of the 1st string in the 2nd item in the inner dict
- i.e.
't'
In Long
Firstly, collections.defaultdict
is a good friend. https://docs.python.org/3/library/collections.html#collections.defaultdict
An example usage:
from collections import defaultdict
x = defaultdict(list)
x['key1'].append('abc')
print(x)
[out]:
defaultdict(<class 'list'>, {'key1': ['abc']})
Next if we kind of initialize the "types" of your dict3
object, it will look something like this:
In human words, dictionary of list of dictionary of list of strings or list of strings, i.e. "messy"/"awkward", see also
In Python types:
from collections import defaultdict
x = defaultdict(lambda: [defaultdict] )
x['key1'].append({'abc': 'foo bar'})
print(x)
[out]:
defaultdict(<function <lambda> at 0x7f75cebca0d0>,
{'key1': [<class 'collections.defaultdict'>, {'abc': 'foo bar'}]})
If we look at first how you can/should initialize dict3
type, you would do something like:
from collections import defaultdict
x = defaultdict(lambda: list(defaultdict(lambda: list)) )
x['key1'].append({'nest_key': ['this is harder']})
print(x)
[out]:
defaultdict(<function <lambda> at 0x7f75cee11940>,
{'key1': [defaultdict(<function <lambda>.<locals>.<lambda> at 0x7f75cf38c4c0>, {}),
{'nest_key': ['this is harder']}]})
And then to access the 'this is harder'
string you would do:
x['key1']
-> returns a list
- i.e.
[{'nest_key': ['this is harder']}]
x['key1'][0]
-> returns first item in the list
- i.e.
{'nest_key': ['this is harder']}
x['key1'][0]['nest_key']
-> returns the nest_key value in the inner dict
- i.e.
['this is harder']
x['key1'][0]['nest_key'][0]
-> returns the first item in the inner dict
- i.e.
'this is harder'
But what happens if I want dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
, where the penultimate inner most list contains a list or string?
To initialize the values, you would have done something like
from collections import defaultdict
x = defaultdict(lambda: list(defaultdict(lambda: list)) )
x['key1'].append({'nest_key': ['this is harder']})
x['key1'][0]['nest_key'].append('this is inserting a string')
x['key1'][0]['nest_key'].append(['this is inserting a list'])
print(type(x['key1'][0]['nest_key'][0])) # "this is harder" -> str
print(type(x['key1'][0]['nest_key'][1])) # "this is inserting a string" -> str
print(type(x['key1'][0]['nest_key'][2])) # ['this is inserting a list'] -> list
[out]:
<class 'str'>
<class 'str'>
<class 'list'>
To summarize the initialization:
Human words
Initialization
Usage
Outputs
dictionary of list
x = defaultdict(list)
x[‘key1’].append(‘abc’)
{‘key1’: [‘abc’]}
dictionary of list of dict
x = defaultdict(lambda: list(defaultdict))
x[‘key1’].append({‘key2′:’abc’})`
{‘key1’: [{‘key2′:’abc’}]}
dictionary of list of dict of list
x = defaultdict(lambda: list(defaultdict(lambda: list)) )
x[‘key1’].append({‘key2’: [‘foo bar’]})
defaultdict(<function at 0x7f75cebbcc10>, {‘key1’: [defaultdict(<function .. at 0x7f75cebbc940>, {}), {‘key2’: [‘foo bar’]}]})
Q: Why do I bother with how the object is initialize if I just to want access an item inside?
A: If you don’t know how the object is initialize, you most probably can’t modify / remove / add items. And also knowing how it’s initialized also helps you understand how to access the items inside.
Food for thought
If access/parsing to the item you want is kinda complicated, is there an easier way to store your data? I.e. is there a better data structure?
dict3[‘key1’] = [{‘nest_key’: [‘this is harder’, [‘hello’]]}]
#answers with a dictionary in a array
dict3[‘key1’][0] = {‘nest_key’: [‘this is harder’, [‘hello’]]}
#answers with just a dictionary (nest_key : 2D array)
dict3[‘key1’][0][‘nest_key’] = [‘this is harder’, [‘hello’]]
#answers with 2D array
dict3[‘key1’][0][‘nest_key’][0] = ‘this is harder’, [‘hello’]
dict3[‘key1’][0][‘nest_key’][0][1] = ‘hello’
Thanks to all for the answers, I have a much better understanding now and understand the terminology better too. I think this sums up what I was trying to learn:
dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
# to extract/grab/print 'this_entry'
print (dict3['key1'][0]['nest_key'][1][0])
The first part [‘key1’][0] is required as it’s a list with just a single element – and this is what we/I want to access. Next, to look at [‘nest_key’] index 1, thus the [1], and here it has again a single element thus requiring the final index of [0].
All makes much more sense and understood where I was going wrong.
So, I’m learning Python (3.x) and it makes a lot of sense until I came across this very awkward progression of dictionaries and trying to get a better understanding of what it is doing. I’m hoping someone could explain in [simple] terms why it does/doesn’t work.
I’m fully aware that there may be better ways of doing what I am trying to do – so I’m open to seeing and learning those too!
I need to extract/print ‘this_entry’ in these examples.
dict1 = {'simple':'this_entry'}
dict2 = {'key1':{'key2':'this_entry'}}
dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
The first two work absolutely fine using
print (dict1['simple'])
print (dict2['key1']['key2'])
BUT the third one is (obviously designed to be) much more tricky, if I use
print (dict3['key1']['nest_key'][1])
It doesn’t work and throws up errors…
So to debug I started with the base level to see what works/what doesn’t.
print (dict3['key1']) #shows [{'nest_key': ['this is harder', ['hello']]}]
print (dict3['key1']['nest_key'][1]) #breaks here showing:
#TypeError: list indices must be integers or slices, not str
Why does it break there? If I add in a ‘pointer’ for the key (is that even the right word?) it kinda works, I just want to know why it breaks if that isn’t used and why the ‘pointer’ is needed in this instance (other than ‘it just doesn’t work without it’).
print (dict3['key1'][0]['nest_key'][1]) #works-ish but shows entry ['hello'], not hello
There’s something obviously missing too as the output is incorrect. I’m guessing understaning why I need the first [0] will also explain on how to extract the entry I desire properly?
Thanks in advance!
With your given dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
, consider running the following statements, and the output they give:
>>> print(dict3["key1"]) # access the value of 'key1', the value is a list
[{'nest_key': ['this is harder', ['this_entry']]}]
>>> print(dict3["key1"][0]) # access the 1st elemtn of the list which is a dict
{'nest_key': ['this is harder', ['this_entry']]}
>>> print(dict3["key1"][0]["nest_key"]) # access the value of 'nest_key, the value is a list
['this is harder', ['this_entry']]
>>> print(dict3["key1"][0]["nest_key"][1]) # get 2nd element of the list, which is another list
['this_entry']
>>> print(dict3["key1"][0]["nest_key"][1][0]) # finaly get the single item in the list, value is a string
this_entry
TL;DR
dict3['key1']
-> returns a list- i.e.
[{'nest_key': ['this is harder']}]
- i.e.
dict3['key1'][0]
-> returns first item in the list- i.e.
{'nest_key': ['this is harder']}
- i.e.
dict3['key1'][0]['nest_key']
-> returns the nest_key value in the inner dict- i.e.
['this is harder']
- i.e.
dict3['key1'][0]['nest_key'][0]
-> returns the first item in the inner dict- i.e.
'this is harder'
- i.e.
dict3['key1'][0]['nest_key'][1]
-> returns the 2nd item in the inner dict- i.e.
['this_entry']
- i.e.
dict3['key1'][0]['nest_key'][1][0]
-> returns the 1st string in the 2nd item in the inner dict- i.e.
'this_entry'
- i.e.
dict3['key1'][0]['nest_key'][1][0][0]
-> returns the 1st character of the 1st string in the 2nd item in the inner dict- i.e.
't'
- i.e.
In Long
Firstly, collections.defaultdict
is a good friend. https://docs.python.org/3/library/collections.html#collections.defaultdict
An example usage:
from collections import defaultdict
x = defaultdict(list)
x['key1'].append('abc')
print(x)
[out]:
defaultdict(<class 'list'>, {'key1': ['abc']})
Next if we kind of initialize the "types" of your dict3
object, it will look something like this:
In human words, dictionary of list of dictionary of list of strings or list of strings, i.e. "messy"/"awkward", see also
In Python types:
from collections import defaultdict
x = defaultdict(lambda: [defaultdict] )
x['key1'].append({'abc': 'foo bar'})
print(x)
[out]:
defaultdict(<function <lambda> at 0x7f75cebca0d0>,
{'key1': [<class 'collections.defaultdict'>, {'abc': 'foo bar'}]})
If we look at first how you can/should initialize dict3
type, you would do something like:
from collections import defaultdict
x = defaultdict(lambda: list(defaultdict(lambda: list)) )
x['key1'].append({'nest_key': ['this is harder']})
print(x)
[out]:
defaultdict(<function <lambda> at 0x7f75cee11940>,
{'key1': [defaultdict(<function <lambda>.<locals>.<lambda> at 0x7f75cf38c4c0>, {}),
{'nest_key': ['this is harder']}]})
And then to access the 'this is harder'
string you would do:
x['key1']
-> returns a list- i.e.
[{'nest_key': ['this is harder']}]
- i.e.
x['key1'][0]
-> returns first item in the list- i.e.
{'nest_key': ['this is harder']}
- i.e.
x['key1'][0]['nest_key']
-> returns the nest_key value in the inner dict- i.e.
['this is harder']
- i.e.
x['key1'][0]['nest_key'][0]
-> returns the first item in the inner dict- i.e.
'this is harder'
- i.e.
But what happens if I want dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
, where the penultimate inner most list contains a list or string?
To initialize the values, you would have done something like
from collections import defaultdict
x = defaultdict(lambda: list(defaultdict(lambda: list)) )
x['key1'].append({'nest_key': ['this is harder']})
x['key1'][0]['nest_key'].append('this is inserting a string')
x['key1'][0]['nest_key'].append(['this is inserting a list'])
print(type(x['key1'][0]['nest_key'][0])) # "this is harder" -> str
print(type(x['key1'][0]['nest_key'][1])) # "this is inserting a string" -> str
print(type(x['key1'][0]['nest_key'][2])) # ['this is inserting a list'] -> list
[out]:
<class 'str'>
<class 'str'>
<class 'list'>
To summarize the initialization:
Human words | Initialization | Usage | Outputs |
---|---|---|---|
dictionary of list | x = defaultdict(list) | x[‘key1’].append(‘abc’) | {‘key1’: [‘abc’]} |
dictionary of list of dict | x = defaultdict(lambda: list(defaultdict)) | x[‘key1’].append({‘key2′:’abc’})` | {‘key1’: [{‘key2′:’abc’}]} |
dictionary of list of dict of list | x = defaultdict(lambda: list(defaultdict(lambda: list)) ) | x[‘key1’].append({‘key2’: [‘foo bar’]}) | defaultdict(<function at 0x7f75cebbcc10>, {‘key1’: [defaultdict(<function .. at 0x7f75cebbc940>, {}), {‘key2’: [‘foo bar’]}]}) |
Q: Why do I bother with how the object is initialize if I just to want access an item inside?
A: If you don’t know how the object is initialize, you most probably can’t modify / remove / add items. And also knowing how it’s initialized also helps you understand how to access the items inside.
Food for thought
If access/parsing to the item you want is kinda complicated, is there an easier way to store your data? I.e. is there a better data structure?
dict3[‘key1’] = [{‘nest_key’: [‘this is harder’, [‘hello’]]}]
#answers with a dictionary in a array
dict3[‘key1’][0] = {‘nest_key’: [‘this is harder’, [‘hello’]]}
#answers with just a dictionary (nest_key : 2D array)
dict3[‘key1’][0][‘nest_key’] = [‘this is harder’, [‘hello’]]
#answers with 2D array
dict3[‘key1’][0][‘nest_key’][0] = ‘this is harder’, [‘hello’]
dict3[‘key1’][0][‘nest_key’][0][1] = ‘hello’
Thanks to all for the answers, I have a much better understanding now and understand the terminology better too. I think this sums up what I was trying to learn:
dict3 = {'key1':[{'nest_key':['this is harder',['this_entry']]}]}
# to extract/grab/print 'this_entry'
print (dict3['key1'][0]['nest_key'][1][0])
The first part [‘key1’][0] is required as it’s a list with just a single element – and this is what we/I want to access. Next, to look at [‘nest_key’] index 1, thus the [1], and here it has again a single element thus requiring the final index of [0].
All makes much more sense and understood where I was going wrong.