Similarly to this question I'd like to create a collections.defaultdict
with a default value of zero for counting elements of a list of tuples:
tuples = [('foo', 5), ('foo', 5), ('bar', -5), ('bar', -2)]
totals = defaultdict(0)
for t in tuples:
totals[t[0]] += t[1]
But defaultdict(0)
says:
TypeError: first argument must be callable
How am I supposed to create a "callable" zero?
A defaultdict works exactly like a normal dict, but it is initialized with a function (“default factory”) that takes no arguments and provides the default value for a nonexistent key. A defaultdict will never raise a KeyError. Any key that does not exist gets the value returned by the default factory.
A defaultdict can be created by giving its declaration an argument that can have three values; list, set or int. According to the specified data type, the dictionary is created and when any key, that does not exist in the defaultdict is added or accessed, it is assigned a default value as opposed to giving a KeyError .
Defaultdict is a sub-class of the dictionary class that returns a dictionary-like object. The functionality of both dictionaries and defaultdict are almost same except for the fact that defaultdict never raises a KeyError. It provides a default value for the key that does not exists.
defaultdict takes a zero-argument callable to its constructor, which is called when the key is not found, as you correctly explained. lambda: 0 will of course always return zero, but the preferred method to do that is defaultdict(int) , which will do the same thing.
The docstring for collections.defaultdict
is pretty confusing at first read:
defaultdict(default_factory[, ...]) --> dict with default factory
The default factory is called without arguments to produce a new value when a key is not present, in getitem only. A defaultdict compares equal to a dict with the same items. All remaining arguments are treated the same as if they were passed to the dict constructor, including keyword arguments.
All this is trying to say is that the default "value" has to be a function, and that that function will be called without arguments whenever it's needed (i.e. whenever a normal dict
would raise a KeyError
.) The docstring refers to this function as default_factory
.
The bit with [, ...]
says that you can optionally pass in an object (or keywords) to use as the defaultdict
's initial dictionary. Thus:
In [26]: x = defaultdict(lambda: 0, a=1, b=2)
In [28]: dict(x)
Out[28]: {'a': 1, 'b': 2}
In [29]: x['c'] # This would normally raise a `KeyError`
Out[29]: 0
In [30]: dict(x) # But here it just adds a new key!
Out[30]: {'a': 1, 'b': 2, 'c': 0}
Any function which returns zero when called with no arguments can be used:
In [31]: int()
Out[31]: 0
Thus, as per the counting example from the docs you could create a defaultdict
with a default value of zero with:
defaultdict(int)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With