Search code examples
pythonlambdadefaultdict

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


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?


Solution

  • 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