Here is an autovivifier which does not require you to set the level at which you want the default factory. When you get an attribute that does not exist on the DefaultHasher, it changes itself into an instance of the default factory:
class DefaultHasher(dict):
def __init__(self, default_factory, change_self=None):
self.default_factory = default_factory
self.change_self = change_self
def change(self, key):
def _change():
x = self.default_factory()
self[key] = x
return x
return _change
def __missing__(self, key):
self[key] = DefaultHasher(self.default_factory,
self.change(key))
return self[key]
def __getattr__(self, name):
result = self.change_self()
return getattr(result, name)
foo = DefaultHasher(set)
foo[1][2][3][4][5].add(1)
print(foo)
# {1: {2: {3: {4: {5: set([1])}}}}}
foo[1][2][3].add(20)
print(foo)
# {1: {2: {3: set([20])}}}
foo[1][3] = foo[1][2]
print(foo)
# {1: {2: {3: set([20])}, 3: {3: set([20])}}}
foo[1][2].add(30)
print(foo)
# {1: {2: set([30]), 3: {3: set([20])}}}