Is There a Clever Way to Pass the Key to Defaultdict's Default_Factory

Accessing key in factory of defaultdict

__missing__ of defaultdict does not pass key to factory function.

If default_factory is not None, it is called without arguments to
provide a default value for the given key, this value is inserted in
the dictionary for the key, and returned.

Make your own dictionary class with custom __missing__ method.

>>> class MyDict(dict):
... def __init__(self, factory):
... self.factory = factory
... def __missing__(self, key):
... self[key] = self.factory(key)
... return self[key]
...
>>> d = MyDict(lambda x: -x)
>>> d[1]
-1
>>> d
{1: -1}

Construct defaultdict by passing default_factory as named parameter

The default_factory parameter of the defaultdict constructor is positional-only and doesn't really have a name. If you try to pass it by name, you are just passing a completely unrelated keyword argument. Since keyword arguments to the defaultdict constructor are interpreted as its initial contents, your dict starts out having a single key "default_factory" whose value is the str type object.

To understand how this works, imagine a function like this:

def func(*args, **kwds):
(default_factory,) = args
for k, v in kwds.items():
print(k, v) # do something with keys and values

If the documentation of this function were to name the positional argument default_factory, that might be a correct description of its meaning, but it would be misleading if it implied that one could pass it as a keyword argument.

Some built-in functions are like that because it is very easy to define positional-only arguments in CPython C code. With defaultdict, it is by design, to allow literally any string key to be used as the part of initial content, without having an exception for a key that happens to be named default_factory.

Create a defaultdict who's key refers to the same numerical value as the key

Using the solution proposed in Is there a clever way to pass the key to defaultdict's default_factory?.

from collections import defaultdict

class keydefaultdict(defaultdict):
def __missing__(self, key):
if self.default_factory is None:
raise KeyError( key )
else:
ret = self[key] = self.default_factory(key)
return ret

d = keydefaultdict(lambda key: key)
print(d[4])
print(d[5])

This outputs

4
5

Trying to get a list with only key value as the defaultvalue in Python using defaultdict

defaultdict will not generate a new value that depends on the key...

you could inherit from dict and overload __missing__:

class MyDict(dict):

def __init__(self):
super().__init__()

def __missing__(self, key):
self[key] = [key]
return self[key]

my_dict = MyDict()

print(my_dict[5]) # -> [5]
print(my_dict) # -> {5: [5]}

there are 2 other answers here that might help:

  • Accessing key in factory of defaultdict
  • Is there a clever way to pass the key to defaultdict's default_factory?

How does collections.defaultdict work?

Usually, a Python dictionary throws a KeyError if you try to get an item with a key that is not currently in the dictionary. The defaultdict in contrast will simply create any items that you try to access (provided of course they do not exist yet). To create such a "default" item, it calls the function object that you pass to the constructor (more precisely, it's an arbitrary "callable" object, which includes function and type objects). For the first example, default items are created using int(), which will return the integer object 0. For the second example, default items are created using list(), which returns a new empty list object.

Use cases for the 'setdefault' dict method

You could say defaultdict is useful for settings defaults before filling the dict and setdefault is useful for setting defaults while or after filling the dict.

Probably the most common use case: Grouping items (in unsorted data, else use itertools.groupby)

# really verbose
new = {}
for (key, value) in data:
if key in new:
new[key].append( value )
else:
new[key] = [value]

# easy with setdefault
new = {}
for (key, value) in data:
group = new.setdefault(key, []) # key might exist already
group.append( value )

# even simpler with defaultdict
from collections import defaultdict
new = defaultdict(list)
for (key, value) in data:
new[key].append( value ) # all keys have a default already

Sometimes you want to make sure that specific keys exist after creating a dict. defaultdict doesn't work in this case, because it only creates keys on explicit access. Think you use something HTTP-ish with many headers -- some are optional, but you want defaults for them:

headers = parse_headers( msg ) # parse the message, get a dict
# now add all the optional headers
for headername, defaultvalue in optional_headers:
headers.setdefault( headername, defaultvalue )


Related Topics



Leave a reply



Submit