How to use a DefaultDict with a lambda expression to make the default changeable?

Question:

DefaultDicts are useful objects to be able to have a dictionary that can create new keys on the fly with a callable function used to define the default value. eg. Using str to make an empty string the default.

>>> food = defaultdict(str)
>>> food['apple']
''

You can also use lambda to make an expression be the default value.

>>> food = defaultdict(lambda: "No food")
>>> food['apple']
'No food'

However you can’t pass any parameters to this lambda function, that causes an error when it tries to be called, since you can’t actually pass a parameter to the function.

>>> food = defaultdict(lambda x: "{} food".format(x))
>>> food['apple']

Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    food['apple']
TypeError: <lambda>() takes exactly 1 argument (0 given)

Even if you try to supply the parameter

>>> food['apple'](12)

Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    food['apple']
TypeError: <lambda>() takes exactly 1 argument (0 given)

How could these lambda functions be responsive rather than a rigid expression?

Asked By: SuperBiasedMan

||

Answers:

Using a variable in the expression can actually circumvent this somewhat.

>>> from collections import defaultdict
>>> baseLevel = 0
>>> food = defaultdict(lambda: baseLevel)
>>> food['banana']
0
>>> baseLevel += 10
>>> food['apple']
10
>>> food['banana']
0

The default lambda expression is tied to a variable that can change without affecting the other keys its already created. This is particularly useful when it can be tied to other functions that only evaluate when a non existant key is being accessed.

>>> joinTime = defaultdict(lambda: time.time())
>>> joinTime['Steven']
1432137137.774
>>> joinTime['Catherine']
1432137144.704
>>> for customer in joinTime:
    print customer, joinTime[customer]

Catherine 1432137144.7
Steven 1432137137.77
Answered By: SuperBiasedMan

Ugly but may be useful to someone:

class MyDefaultDict(defaultdict):
    def __init__(self, func):
        super(self.__class__, self).__init__(self._func)
        self.func = func

    def _func(self):
        return self.func(self.cur_key)
        
    def __getitem__(self, key):
        self.cur_key = key
        return super().__getitem__(self.cur_key)
Answered By: Oren
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.