13

I have an empty array.

I want to assign a value like this: array[key][subkey] = 'value'

This produces a KeyError as array[key] does not exist yet.

What do I do? I tried the following...

array['key'] = None
array['key']['subkey'] = 'value'

TypeError: 'NoneType' object does not support item assignment

I tried:

array['key'] = []
array['key']['subkey'] = 'value'

TypeError: list indices must be integers, not str

I tried:

array['key'] = ['subkey']
array['key']['subkey'] = 'value'

TypeError: list indices must be integers, not str

So what do I do?

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
Gray Adams
  • 3,927
  • 8
  • 32
  • 39

5 Answers5

29

You could use collections.defaultdict, passing the default factory as dict:

>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> d['key']['subkey'] = 'value'
>>> d
defaultdict(<type 'dict'>, {'key': {'subkey': 'value'}})

To apply further levels of nesting, you can create a defaultdict that returns defaultdicts to a n-th depth of nesting, using a function, preferably anonymous, to return the nested default dict(s):

>>> d = defaultdict(lambda: defaultdict(dict))
>>> d['key']['subkey']['subsubkey'] = 'value'
>>> d
defaultdict(<function <lambda> at 0x104082398>, {'key': defaultdict(<type 'dict'>, {'subkey': {'subsubkey': 'value'}})})

Example shows nesting up to depth n=1

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
7

You are using a [] list literal not a {} dict literal:

array['key'] = {}
array['key']['subkey'] = 'value'

But this isn't very useful in a loop.
In a loop you could test if 'key' is not in array - which is a cheap operation (O(1) lookup):

if 'key' not in array:
    array['key'] = {}
array['key']['subkey'] = 'value'

But you can use setdefault() to do the same thing and give key a default value if it doesn't already have a value, e.g.:

array.setdefault('key', {})['subkey'] = 'value'

And if this looks ugly, then you can always use collection.defaultdict.

fidekild
  • 161
  • 3
  • 13
AChampion
  • 29,683
  • 4
  • 59
  • 75
4

You could use a nested defaultdict like this, goes arbitrarily deep automatically:

>>> from collections import defaultdict
>>> def ndd():
        return defaultdict(ndd)

>>> array = ndd()
>>> array['key']['subkey']['subsubkey'] = 'value'
>>> array
defaultdict(<function ndd at 0x041AC270>,
            {'key': defaultdict(<function ndd at 0x041AC270>,
                                {'subkey': defaultdict(<function ndd at 0x041AC270>,
                                                       {'subsubkey': 'value'})})})

(I indented the output for readability.)

Originally saw that from severb.

Stefan Pochmann
  • 27,593
  • 8
  • 44
  • 107
  • The issue with using it is that the last value can't be of type list for example, can you find a solution for that ? array['key']['subkey']['subsubkey'].append(msg) will raise AttributeError which need to be handled array['key']['subkey']['subsubkey'] = [msg] – Sion C May 09 '23 at 13:50
3

I have used this alternative way before (edit: although I preferred Moses Koledoye answer):

array = {}

try:
    array[key][subkey] = 'value'
except KeyError:
    array[key] = {}
    array[key][subkey] = 'value'
Anton vBR
  • 18,287
  • 5
  • 40
  • 46
0

Can the below codes be used as a different method?

>>> array = {}
>>> array["key"] = {"subkey": "value"}
>>> array
{'key': {'subkey': 'value'}}
>>> 
dildeolupbiten
  • 1,314
  • 1
  • 15
  • 27
  • 1
    Where I have used it (creating dictionaries inside loops) this is not an option as it does not "fill" the dictionary but rather creates a new one. – Anton vBR Jul 09 '17 at 01:33