pythonpattern-matching

How to use pattern matching with objects?


I have the following python code that converts an object into a view-like mapping.

from collections.abc import Mapping

class ObjView(Mapping):
    def __init__(self, obj):
        self.obj = obj

    def __contains__(self, key):
        return key in dir(self.obj)
        #return hasattr(self.obj, key)  # Better, see the answers.

    def __getitem__(self, key):
        return getattr(self.obj, key)

    def __len__(self):
        return len(dir(self.obj))
    
    def __iter__(self):
        return iter(dir(self.obj))

The use-case I have in mind is pattern-matching class elements:

class Bird():
    ...
    def type(self):
        match ObjView(self):
            case {'quacks': True}:
                return 'Duck'
            case {'wings': [w1, w2, w3], 'flies': False, 'colors': ['black', 'white']}:
                return 'Three-winged penguin'
            ...

The good thing is that an AttributeError is raised if the key is not an attribute, instead of the KeyError that gets caught in dict pattern-matching. I know the example is a bit silly, but in my situation a lot of the attributes are tuples of length 0 to 3, and I want to pattern-match them.

Question. Can I achieve this type of pattern-matching behaviour using builtins instead of creating a custom ObjView class?


Solution

  • For the most part, you are re-implementing class patterns.

    class Bird():
        ...
        def type(self):
            match self:
                case Bird(quacks=True):
                    return 'Duck'
                case Bird(wings=[_, _, _], flies=False, colors=['black', 'white']):
                    return 'Three-winged penguin'
                ...
    

    Note that pattern-matching, as provided by the match statement, is not always a replacement for an ordinary if-elif statement, though guards can help. For instance, if you want a bird with at most three wings, instead of exactly three wings, you can do something like

    case Bird(wings=list(), flies=False, colors=['black', 'white']) if len(wings) <= 3: