He is explicitly copying the behaviour of the standard library defaultdict here. One clear motivation is thus:
Suppose I have an empty defaultdict(list).
I do the following:
d = defaultdict(list)
l = d['foo']
According to no-mutate-on-get semantics, we now have an empty list, and d is still empty.
So what happens if I do:
l.append('bar')
You might expect the dict to now be populated with {'foo': ['bar']}. However, the dict has no way of knowing (without a large amount of additional tricks) whether the new list that it previously returned had been later modified. So what would actually happen would be nothing, d would remain empty. Worse, now your behaviour is different depending on whether or not the 'foo' key was already present (if it was, you would've gotten a reference to the actual value and mutated it, and d would have changed as a result).
As I see it, there are only two sane solutions to this problem: Either immediately store every generated value so any subsequent mutation is saved (the solution used here)...or require that your default values be immutable.
Suppose I have an empty defaultdict(list). I do the following:
According to no-mutate-on-get semantics, we now have an empty list, and d is still empty. So what happens if I do: You might expect the dict to now be populated with {'foo': ['bar']}. However, the dict has no way of knowing (without a large amount of additional tricks) whether the new list that it previously returned had been later modified. So what would actually happen would be nothing, d would remain empty. Worse, now your behaviour is different depending on whether or not the 'foo' key was already present (if it was, you would've gotten a reference to the actual value and mutated it, and d would have changed as a result).As I see it, there are only two sane solutions to this problem: Either immediately store every generated value so any subsequent mutation is saved (the solution used here)...or require that your default values be immutable.