Adam Allport Adam Allport - 15 days ago 5
Python Question

Accessing Python Dictionarys with ["key","key2"]

I am developing a python utility. Part of this is to generate an index of files

Is it possible within python to access a multidimensional dictionary dynamically without knowing the depth.

ie if i had the example data:

example = {'main': {'2': {'2': '2-2', '1': '2-1'}, '1': {'2': '1-2', '1': '1-1'}}}


Is there a way i can access elements with something similar to
example["main","2","1"]
and it return
2-1
?
I am aware i can write my own diving algorithms but my experience shows you cannot then write to the value.

Answer

If you absolutely positively need to do it that way, you'll need to roll your own dict class. Luckily you can inherit everything but __getitem__ from dict.

class MyDict(dict):
    def __getitem__(self, keys):
        if isinstance(keys, str):
            # this special-case saves you if you try to do normal indexing
            # on a string.
            return super().__getitem__(keys)
        cur = self
        for key in keys:
            cur = cur.get(key, {})
            # the default option here returns an empty dict instead
            # of raising a KeyError. That might not be what you want
        return cur

Note that this removes your ability to key by tuple, so key/vals like {("some", "tuple", "values"): "any value"} will be inaccessible unless specifically coded for. That might look something like...

...
        for i, key in enumerate(keys):
            if keys[i:] in cur:
                return cur[keys[i:]]
            cur = cur.get(key, {})

You can then cast your mapping to this new dict and search that way.

example = {'main': {'2': {'2': '2-2', '1': '2-1'}, '1': {'2': '1-2', '1': '1-1'}}}
result = MyDict2(example)['2', '2', '1']

You mention having to set values by this as well, in which case also inherit __setitem__.

class MyDict(dict):
    def __getitem__(self, keys):
        # as above
    def __setitem__(self, keys, value):
        if isinstance(keys, str):
            super().__setitem__(keys, value)
        cur = self
        for key in keys[:-1]:
            cur = cur.setdefault(key, {})
        cur[keys[-1]] = value