I'd like to be able to deconstruct `complex` objects into their real and imaginary components using a `match` statement, like: match 1-4j: case complex(a, b): print(f"{a=} {b=}") This would just require setting complex.__match_args__ = ("real", "imag") The other builtin type I think should be able to do this is `slice`: match slice(1, -1, None): case slice(start, stop, step): print(f"{start=} {stop=} {step=}") which would require slice.__match_args__ = ("start", "stop", "step") This would be useful, for example, in custom __getattr__ methods which need to handle slices. def __getattr__(self, arg): match arg: case slice(start, stop, end): ... # handle slice case n if isinstance(n, int): ... # handle single index case _: raise TypeError There are various other builtin types which could conceivably have __match_args__ defined, but with varying degrees of usefulness: range, property, staticmethod, classmethod, memoryview, types.GenericAlias, types.UnionType, types.CodeType, types.FunctionType, types.MethodType, types.ModuleType, types.GenericAlias, types.FrameType, types.TracebackType, types.CellType But none of these are really that important, I think. What do you think? Patrick
Some background: the reason we decided not to do this is because cases are allowed to omit positional sub-patterns when matching. For example, it would be possible to write this: ```
match n: ... case complex(42j): ... ... ...
Read that carefully! It would match any complex number with a *real* part equal to 42j. Which is to say, it would actually match *nothing*.
I personally prefer the (admittedly wordier) status quo:
match n: ... case complex(real=r, imag=i): ... ... ...
The problem with slice is much bigger, though: the expected attributes actually change depending on how many "args" there are, so a static mapping just isn't possible. Again, the status quo is much more explicit, for better or worse:
match i: ... case slice(start=lo, stop=hi, step=by): ... pass ...
At one point, the original pattern matching proposal (and implementation) included support for a `__match_args_required__` attribute that could optionally be provided alongside `__match_args__`. With that, you could then set `complex.__match_args__ = ("real", "imag")` and `complex.__match_args_required__ = 2`, and get an error like:
match n: ... case complex(42j): ... ... ... TypeError: complex() requires 2 positional sub-patterns (1 given)
Similarly, you could set `slice.__match_args__ = ("start", "stop", "step")` and `slice.__match_args_required__ = 2`, and get an error like:
match i: ... case slice(stop): ... ... ... TypeError: slice() requires at least 2 positional sub-patterns (1 given)
We eventually dropped `__match_args_required__` to simplify the proposal, though. For anyone curious, all of our discussions during this period are archived in the issue tracker at: https://github.com/gvanrossum/patma/issues.
Brandt
participants (2)
-
Brandt Bucher
-
Patrick Reader