最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to use pattern matching with objects? - Stack Overflow

programmeradmin2浏览0评论

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?

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?

Share Improve this question edited Mar 11 at 16:27 bfontaine 19.9k14 gold badges79 silver badges120 bronze badges asked Mar 11 at 13:40 KeplertoKeplerto 1215 bronze badges 2
  • 2 Why not use class patterns like case Bird(quacks=True)? – chepner Commented Mar 11 at 13:46
  • @chepner Thanks! I did not know that was possible. – Keplerto Commented Mar 11 at 14:09
Add a comment  | 

2 Answers 2

Reset to default 2

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:

Yes,

This is perfectly valid.

You might however prefer to use hasattr instead of dir in __contains__, as the later will serialize all of one objects attributes on a list, and them match your your attribute linearly.

Otherwise it can be more or less specialized - for example, it might be useful not to expose any attributes starting with a _ , so that the "viewed" class can have internal attributes. (this can be accomplished with if statements in your __contains__, __getitem__ and __iter__.)

    def __contains__(self, key):
        return hasattr(self.obj, key)
   
发布评论

评论列表(0)

  1. 暂无评论