Enhancement About "Deep" Appending In Dicts

Proposal (Not A Huge Thing)

some_dict = {}
some_dict[key1][key2] = value

I am speaking according to the example, I propose you to support appending in cases where some_dict[key1] doesn’t exist.

To my idea, it should append an empty dict value -as default- to some_dict (with the key key1) which contains the key-value pair key2: value:

some_dict == { key1:{key2:value} }

Benefit

Serial append which I find great for complex dict in dict structures. (You don’t have to check if parent key exists)

dict1 = {}
"[...UPDATING DICT1...]"
## dict1.setdefault("abc", {}) # make it sure that "abc" exists.
dict1["abc"]["def"] = 123

What are your thoughts?

Thanks for reading.

There are some ways to create this kind of structure. You can make a recursive defaultdict (but some find the defaultdict output annoying). Or you could create a class that defines __missing__ and returns a new dict.

One problem is that it’s hard to distinguish between assigning to a position and just checking for existence. So just checking a value can bring a tree into existence (which often isn’t desired). You lose the protections you would otherwise have to warn when accessing unknown keys.

1 Like

I’m against this. It would make typos in dictionary keys silently give wrong answers, rather than failing immediately like they currently do.

It’s possible to do this currently with a defaultdict(dict)[1], which explicitly signals that you want this behaviour. Making that behaviour part of the standard dictionary would mean that we’d need to add a new dictionary type that acted like dictionaries currently do - because there are definitely people who rely on the current behaviour. So all you’d be doing in practice is swapping which of dict and defaultdict(dict) is builtin, and which is a stdlib type. That’s effectively no benefit, for a huge backward compatibility cost.


  1. Or, as @BowlOfRed notes, a recursive version if you want it to happen indefinitely deeply. ↩︎

1 Like

Thank you both for your thoughts. Yes, it seems possible to create a custom class for this goal. I just wanted to see that as a feature.

I can definitely see the point here. Thank you both again.

Also consider a separate utility class used just for updating. This way, the dict won’t have surprising superpowers after it’s built. Something like:

class Updater:
    def __init__(self, target):
        self.target = target
    def __getitem__(self, key):
        return Updater(self.target.setdefault(key, {}))
    def __setitem__(self, key, value):
        self.target[key] = value

dict1 = {}
Updater(dict1)["abc"]["def"] = 123

do you mean something like this,

from collections import defaultdict
x = defaultdict(lambda: defaultdict(dict, {'def': 123}))
x['abc']

gives,

defaultdict(dict, {'def': 123})

@vainaixr
Eh, not exactly. Maybe more like this:

from collections import defaultdict
d = defaultdict(lambda: defaultdict(dict))
d["abc"]["def"] = 10
print(d)

If the parent key (‘abc’ in the example) is missing, appends a dict and continues to the process. -inside the new dict value-

isn’t it the same thing,

from collections import defaultdict
x = defaultdict(lambda: defaultdict(dict, {'def': 123}))
x['abc']['def']

would give,

123

if you set,

x['ghi'] = 10
x

would give,

defaultdict(<function __main__.<lambda>>,
            {'abc': defaultdict(dict, {'def': 123}), 'ghi': 10})

if the key is not present and you attempt at accessing it, the default value would be {'def': 123}, otherwise you could give a value of your choice, like for the key 'ghi' the value is 10

Yes, but there is a slight difference. My main goal/request was not to set a custom dict as a default value; just an empty one. I believed this would bring out some easiness when working with complex structures. It doesn’t raise a key error, instead it adds an empty dict. There won’t be any need to check if parent key exists, or you won’t be asked for adding the parent key first.
So, there will not be any need for using setdefaults.

some_dict = {}

some_dict["weight"]["people"]["sandra"] = 100_000_000

instead of:

some_dict["weight"] = {"people": {"sandra" : 100_000_000} }

or instead of:

some_dict.setdefault("weight", { "people": {} } ) 
some_dict["weight"]["people"]["sandra"] = 100_000_000

PS:


I have forgotten to make the default dict recursive in my last post’s example, sorry for that.


Btw, our tiny codes(the ones which include defaultdict) are just simple examples, they have some defects.


I have realized that it is a foolish idea with its current state since it literally blocks KeyError. But, adding a new default arg to __setitem__ can still be considered. (strict is the name, True is the default value).