Search code examples
pythondictionarylambdaparameters

defaultdict with a lambda that uses the key as a parameter


I was trying to create a defaultdict where the key was used as a parameter to create a classed object, but I get a TypeError when I try. I've put a minimum example below

from collections import defaultdict

class Intersection:
    def __init__(self,xy):
        self.position = xy
        self.stuff = []
    def add(self,stuff):
        self.stuff.append(stuff)

intersections: defaultdict[tuple[int,int],Intersection] = defaultdict(lambda xy: Intersection(xy))
intersections[(1,2)].add('stuff')
>>> TypeError: <lambda>() missing 1 required positional argument: 'xy'

I googled the error and it came up with this post which is close to what I'm trying to do but just far enough not to be able to answer how to do this


Solution

  • After searching around for a while I stumbled upon this github issue asking about this functionality. It seems this will not be added to defaultdict but it CAN be done using the __missing__() method on dict.

    Since this was difficult for me to find I wanted to post it somewhere where other people (including future me) can find it.

    class Intersection:
        def __init__(self,xy):
            self.position = xy
            self.stuff = []
        def add(self,stuff):
            self.stuff.append(stuff)
    
    class KeyDict(dict):
        def __missing__(self,key):
            self[key] = Intersection(key)
            return self[key]
    
    intersections = KeyDict()
    intersections[(1,2)].add('stuff')
    print(intersections)
    >>> {(1, 2): <__main__.Intersection object at 0x000001A3226E0F70>}
    

    Note: This works with parameter expansion too. Originally I had it included but I factored it out for clarity