Python dict with default value based on key
Question:
I need a dictionary that is automatically filled with a default value for each accessed key that is missing. I’ve found defaultdict
and some other ways to achieve this, but the problem in my case is that I want the default value for each key to be specific to the key itself.
For example, with defaultdict
I can achieve something like this:
from collections import defaultdict
d = defaultdict(lambda: 5)
> d[1] = 3
> d[1]
> 3
> d[2]
> 5
But what if I need the default value for each accessed missing key to be for example key + 5
? Something like:
from collections import defaultdict
d = defaultdict(lambda key: key + 5) # <-- This does not work as defaultdict expects lambda function to be without any parameters
> d[1] = 3
> d[1]
> 3
> d[2]
> 7 <- Calculated from accessed key + 5 (2+5)
> d[5]
> 10 <- Calculated from accessed key + 5 (5+5)
Is there a clean, builtin way to achieve what I need? I know that I can create a subclass of dict and implement this specific functionality at __getitem__
level, but I want to avoid that if possible.
I couldn’t find a solution in other answers, so sorry if it is still a duplicate.
Answers:
I don’t think there is a builtin way of doing this. However, instead of subclassing dict
and change getitem
, you can subclass defaultdict
itself to tell __missing__()
to call the default_factory
with an argument (the key
), rather than without args. It is still a custom class, not a builtin, but it should be quite efficient still.
from collections import defaultdict
class DefaultDict(defaultdict):
def __missing__(self, key):
return self.default_factory(key)
Then:
d = DefaultDict(lambda key: key + 5)
d[2]
# 7
You can also use a function like this
dic = {}
DEFAULT_VALUE = 5
def dict_get(item):
try:
return [dic[item]]
except:
dic[int(item)] = DEFAULT_VALUE + int(item)
return DEFAULT_VALUE + int(item)
print(dict_get(10))
Here is how to do it without creating any class. You can use dict.setdefault:
d = {}
d_default_factory = lambda key: key + 5
d.setdefault(2, d_default_factory(2))
# 7
Optionally, you could save the default factory as an entry in the dictionary, like so:
d = {'default_factory': lambda key: key + 5}
d.setdefault(2, d['default_factory'](2))
# 7
I need a dictionary that is automatically filled with a default value for each accessed key that is missing. I’ve found defaultdict
and some other ways to achieve this, but the problem in my case is that I want the default value for each key to be specific to the key itself.
For example, with defaultdict
I can achieve something like this:
from collections import defaultdict
d = defaultdict(lambda: 5)
> d[1] = 3
> d[1]
> 3
> d[2]
> 5
But what if I need the default value for each accessed missing key to be for example key + 5
? Something like:
from collections import defaultdict
d = defaultdict(lambda key: key + 5) # <-- This does not work as defaultdict expects lambda function to be without any parameters
> d[1] = 3
> d[1]
> 3
> d[2]
> 7 <- Calculated from accessed key + 5 (2+5)
> d[5]
> 10 <- Calculated from accessed key + 5 (5+5)
Is there a clean, builtin way to achieve what I need? I know that I can create a subclass of dict and implement this specific functionality at __getitem__
level, but I want to avoid that if possible.
I couldn’t find a solution in other answers, so sorry if it is still a duplicate.
I don’t think there is a builtin way of doing this. However, instead of subclassing dict
and change getitem
, you can subclass defaultdict
itself to tell __missing__()
to call the default_factory
with an argument (the key
), rather than without args. It is still a custom class, not a builtin, but it should be quite efficient still.
from collections import defaultdict
class DefaultDict(defaultdict):
def __missing__(self, key):
return self.default_factory(key)
Then:
d = DefaultDict(lambda key: key + 5)
d[2]
# 7
You can also use a function like this
dic = {}
DEFAULT_VALUE = 5
def dict_get(item):
try:
return [dic[item]]
except:
dic[int(item)] = DEFAULT_VALUE + int(item)
return DEFAULT_VALUE + int(item)
print(dict_get(10))
Here is how to do it without creating any class. You can use dict.setdefault:
d = {}
d_default_factory = lambda key: key + 5
d.setdefault(2, d_default_factory(2))
# 7
Optionally, you could save the default factory as an entry in the dictionary, like so:
d = {'default_factory': lambda key: key + 5}
d.setdefault(2, d['default_factory'](2))
# 7