You can expand this a bit to make it n-level depth (until you blow the stack).
def tree():
return defaultdict(tree)
>>> t = tree()
>>> t['a']['b']['c'] = 10
>>> t
defaultdict(<function tree at 0x10c40df28>, {'a':
defaultdict(<function tree at 0x10c40df28>, {'b':
defaultdict(<function tree at 0x10c40df28>, {'c': 10})})})
It’s really 2 ways to do it, and the defaultdict way (I believe) would have less allocations in a deeply nested loop (since this version has to create a {} and a [] every time).
Also setdefault causes confusion for less experienced users in a way that the defaultdict format does not.
I came across this once in Peter Norvig's Udacity CS 212 course - I think it was in the discussion forums for one of the lessons. My head promptly exploded.