Unpacking a class

Viewed 388

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())
3 Answers

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))
def bar(a, b):
   return a + b
  
class FooClass:
    def __init__(self, a, b):
        self.a = a
        self.b = b

f = FooClass(1, 2)

print(bar(*f.__dict__.values()))

# print(bar(**f.__dict__)) # Also works

Output:

3

Aside from reyling on vars(f) or f.__dict__, you could use a dataclass.

from dataclasses import dataclass, asdict

@dataclass
class FooClass:
    a: int
    b: int

Demo:

>>> f = FooClass(1, 2)
>>> asdict(f)
{'a': 1, 'b': 2}
Related