Search code examples
pythonargument-unpacking

Unpacking a class


I'd like to make a class that unpacks it's objects like a dictionary.

For example, with a dictionary you can do this

foo = {
  "a" : 1
  "b" : 2
}

def bar(a,b):
   return a + b

bar(**foo)

outputs 3

And I'd like to be able to do this

class FooClass:
    def __init__(self):
        self.a = a
        self.b = b

f = FooClass()
bar(**f)

and have it output 3

This is the most related question I could find but it doesn't address this so I'm thinking it might not be possible.


Currently what my solution would be this:

class FooClass:
    def __init__(self):
        self.a = a
        self.b = b

    def to_dict(self):
        return {
          "a" : self.a,
          "b" : self.b
        }
f = FooClass()
bar(**f.to_dict())

Solution

  • As pointed out in the comments, writing a conformant subclass of the collections.abc.Mapping abstract class is the way to go. To (concretely) subclass this class, you need to implement __getitem__, __len__, and __iter__ to behave consistently like a dictionary would. So that means __getitem__ expects a string, __iter__ returns an iterable of strings, etc.

    For a simple example, we'll simply delegate all of these to self.__dict__, but in real code you'd likely want to do something more refined.

    from collections.abc import Mapping
    
    class FooClass(Mapping):
    
        def __init__(self, a, b):
            self.a = a
            self.b = b
    
        def __getitem__(self, x):
            return self.__dict__[x]
    
        def __iter__(self):
            return iter(self.__dict__)
    
        def __len__(self):
            return len(self.__dict__)
    
    def bar(a, b):
        return a + b
    
    foo = FooClass(40, 2)
    print(bar(**foo))