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?
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: