Open ilevkivskyi opened 4 years ago
Another minor thing, one can obviously opt-out of pattern matching using __match__ = None
, similar to __hash__ = None
.
I am in full agreement here.
Oh, object.__match__
must be a class method, else the isinstance()
check has nothing to check for. (I think in your PEP, __match__
is implicitly a class method; I'm not sure I like that (I can imagine a static method being useful in some cases, or even an instance method).
Just to clarify, what are the cases where static/instance methods will be useful? I am a bit uncomfortable with using instance method for this because if one writes:
class Foo:
def __match__(self):
...
then in this code
x = 1
match x:
as Foo():
...
we will call Foo.__match__(1)
, i.e. the self
argument will be and int
, not a Foo
. So I imagine people who will read the code may be surprised by a check like if isinstance(self, Foo)
.
Also there are precedents where methods that are called on classes by the interpreter are implicitly class methods, like __new__()
, __init_subclass__()
, etc., and __match__()
is very similar to __new__()
.
I think I see a use case for instance methods: defining a __match__()
in metaclass. Potentially some ORMs might want to do this.
Also, EIBTI here, I think.
From my understanding of #8 the current proposal is that there is no
object.__match__()
so that by default trying to match againstFoo()
raises an error. In my draft notes I have some spec forobject.__match__()
because I think it is important to provide some "out of the box" experience, so that we don't force owners of existing codebases to add dummy@matchable
class decorators to dozens of their classes.I agree that in its current version my spec is an overkill (like it uses
__slots__
as__match_args__
etc). But I think there is a way to provide a minimal safeobject.__match__()
implementation if we make one tweak to the spec: missing attribute should raise an exception rather than fail a match.The main argument is that failing a match would hide hard to debug typos. Essentially, my idea is that matching
x
againstFoo(bar=12)
should be equivalent toisinstance(x, Foo) and x.bar == 12
, and the latter would raise an exception in case of a typo in the attribute name. Also, it is rare for classes to have optional attributes unset, they are set to some default value (likeNone
) much often. Finally, it is easy to override this behaviour by explicitly adding an attribute to__match_args__
indicating we shouldn't raise if such attribute was requested.Note: this would be different from matching against a mapping, where not having a key is just a failed match. But I think this is normal, classes can behave stricter than just syntax sugar for dictionaries, more like
TypedDicts
in typing terminology for which the set of allowed keys is fixed.If we agree with the stricter semantics then IIUC
object.__match__()
would just perform anisinstance()
check and returnself
. So that match by name will work, but positional match will raise an exception. Btw I am not sure the latter is a conclusion of #8, but I think it probably should be. If the proxy object doesn't specify__match_args__
, the positional match should raise (with an error message like<class 'Foo'> doesn't support match by position
) rather than fail the match.