default behavior
Steven D'Aprano
steve at REMOVE-THIS-cybersource.com.au
Fri Jul 30 23:47:49 EDT 2010
On Fri, 30 Jul 2010 08:34:52 -0400, wheres pythonmonks wrote:
> Sorry, doesn't the following make a copy?
>
>>>>> from collections import defaultdict as dd x = dd(int)
>>>>> x[1] = 'a'
>>>>> x
>> defaultdict(<type 'int'>, {1: 'a'})
>>>>> dict(x)
>> {1: 'a'}
>>
>>
>>
>
> I was hoping not to do that -- e.g., actually reuse the same underlying
> data.
It does re-use the same underlying data.
>>> from collections import defaultdict as dd
>>> x = dd(list)
>>> x[1].append(1)
>>> x
defaultdict(<type 'list'>, {1: [1]})
>>> y = dict(x)
>>> x[1].append(42)
>>> y
{1: [1, 42]}
Both the defaultdict and the dict are referring to the same underlying
key:value pairs. The data itself isn't duplicated. If they are mutable
items, a change to one will affect the other (because they are the same
item). An analogy for C programmers would be that creating dict y from
dict y merely copies the pointers to the keys and values, it doesn't copy
the data being pointed to.
(That's pretty much what the CPython implementation does. Other
implementations may do differently, so long as the visible behaviour
remains the same.)
> Maybe dict(x), where x is a defaultdict is smart? I agree that a
> defaultdict is safe to pass to most routines, but I guess I could
> imagine that a try/except block is used in a bit of code where on the
> key exception (when the value is absent) populates the value with a
> random number. In that application, a defaultdict would have no random
> values.
If you want a defaultdict with a random default value, it is easy to
provide:
>>> import random
>>> z = dd(random.random)
>>> z[2] += 0
>>> z
defaultdict(<built-in method random of Random object at 0xa01e4ac>, {2:
0.30707092626033605})
The point which I tried to make, but obviously failed, is that any piece
of code has certain expectations about the data it accepts. If take a
function that expects an int between -2 and 99, and instead decide to
pass a Decimal between 100 and 150, then you'll have problems: if you're
lucky, you'll get an exception, if you're unlucky, it will silently give
the wrong results. Changing a dict to a defaultdict is no different.
If you have code that *relies* on getting a KeyError for missing keys:
def who_is_missing(adict):
for person in ("Fred", "Barney", "Wilma", "Betty"):
try:
adict[person]
except KeyError:
print person, "is missing"
then changing adict to a defaultdict will cause the function to
misbehave. That's not unique to dicts and defaultdicts.
> Besides a slightly different favor, does the following have applications
> not covered by defaultdict?
>
> m.setdefault('key', []).append(1)
defaultdict calls a function of no arguments to provide a default value.
That means, in practice, it almost always uses the same default value for
any specific dict.
setdefault takes an argument when you call the function. So you can
provide anything you like at runtime.
> I think I am unclear on the difference between that and:
>
> m['key'] = m.get('key',[]).append(1)
Have you tried it? I guess you haven't, or you wouldn't have thought they
did the same thing.
Hint -- what does [].append(1) return?
--
Steven
More information about the Python-list
mailing list