PEP 622: Updates from Python-Dev (#1472)
In particular __match_args__ is simplified and the default processing only applies to certain built-in types.
This commit is contained in:
parent
c49bcbb1a0
commit
a106177241
45
pep-0622.rst
45
pep-0622.rst
|
@ -142,10 +142,9 @@ A simplified, approximate grammar for the proposed syntax is::
|
||||||
pattern: NAME ':=' or_pattern | or_pattern
|
pattern: NAME ':=' or_pattern | or_pattern
|
||||||
or_pattern: closed_pattern ('|' closed_pattern)*
|
or_pattern: closed_pattern ('|' closed_pattern)*
|
||||||
closed_pattern:
|
closed_pattern:
|
||||||
| name_pattern
|
|
||||||
| literal_pattern
|
| literal_pattern
|
||||||
|
| name_pattern
|
||||||
| constant_pattern
|
| constant_pattern
|
||||||
| group_pattern
|
|
||||||
| sequence_pattern
|
| sequence_pattern
|
||||||
| mapping_pattern
|
| mapping_pattern
|
||||||
| class_pattern
|
| class_pattern
|
||||||
|
@ -496,7 +495,7 @@ guard succeeds. So this will work::
|
||||||
|
|
||||||
values = [0]
|
values = [0]
|
||||||
|
|
||||||
match value:
|
match values:
|
||||||
case [x] if x:
|
case [x] if x:
|
||||||
... # This is not executed
|
... # This is not executed
|
||||||
case _:
|
case _:
|
||||||
|
@ -571,26 +570,16 @@ The procedure is as following:
|
||||||
sub-patterns succeed, the overall class pattern match succeeds.
|
sub-patterns succeed, the overall class pattern match succeeds.
|
||||||
|
|
||||||
* If there are match-by-position items and the class has a
|
* If there are match-by-position items and the class has a
|
||||||
``__match_args__`` which is not ``None``, the item at position ``i``
|
``__match_args__``, the item at position ``i``
|
||||||
is matched against the value looked up by attribute
|
is matched against the value looked up by attribute
|
||||||
``__match_args__[i]``. For example, a pattern ``Point2D(5, 8)``,
|
``__match_args__[i]``. For example, a pattern ``Point2D(5, 8)``,
|
||||||
where ``Point2D.__match_args__ == ["x", "y"]``, is translated
|
where ``Point2D.__match_args__ == ["x", "y"]``, is translated
|
||||||
(approximately) into ``obj.x == 5 and obj.y == 8``.
|
(approximately) into ``obj.x == 5 and obj.y == 8``.
|
||||||
|
|
||||||
* When ``__match_args__`` is missing (as is the default) or ``None``, a single
|
|
||||||
positional sub-pattern is allowed to be passed to the call. Rather than being
|
|
||||||
matched against any particular attribute on the proxy, it is instead matched
|
|
||||||
against the proxy itself. This creates default behavior that is useful and
|
|
||||||
intuitive for most objects:
|
|
||||||
|
|
||||||
* ``bool(False)`` matches ``False`` (but not ``0``).
|
|
||||||
* ``tuple((0, 1, 2))`` matches ``(0, 1, 2)`` (but not ``[0, 1, 2]``).
|
|
||||||
* ``int(i)`` matches any ``int`` and binds it to the name ``i``.
|
|
||||||
|
|
||||||
* If there are more positional items than the length of ``__match_args__``, an
|
* If there are more positional items than the length of ``__match_args__``, an
|
||||||
``ImpossibleMatchError`` is raised.
|
``ImpossibleMatchError`` is raised.
|
||||||
|
|
||||||
* If the ``__match_args__`` attribute is absent on the matched class or ``None``,
|
* If the ``__match_args__`` attribute is absent on the matched class,
|
||||||
but more than one positional item appears in a match,
|
but more than one positional item appears in a match,
|
||||||
``ImpossibleMatchError`` is also raised. We don't fall back on
|
``ImpossibleMatchError`` is also raised. We don't fall back on
|
||||||
using ``__slots__`` or ``__annotations__`` -- "In the face of ambiguity,
|
using ``__slots__`` or ``__annotations__`` -- "In the face of ambiguity,
|
||||||
|
@ -602,18 +591,30 @@ The procedure is as following:
|
||||||
two cases are distinguished:
|
two cases are distinguished:
|
||||||
|
|
||||||
* If an attribute is missing on the proxy and the class being matched
|
* If an attribute is missing on the proxy and the class being matched
|
||||||
has no ``__match_args__`` attribute (or it is ``None``), the match
|
has no ``__match_args__`` attribute, the match
|
||||||
fails. This allows one to write ``case object(name=_)`` to
|
fails. This allows one to write ``case object(name=_)`` to
|
||||||
implement a check for the presence of a given attribute, or ``case
|
implement a check for the presence of a given attribute, or ``case
|
||||||
object(name=var)`` to check for its presence and extract its value.
|
object(name=var)`` to check for its presence and extract its value.
|
||||||
|
|
||||||
* If an attribute is missing and the class has a ``__match_args__``
|
* If an attribute is missing and the class has a ``__match_args__``,
|
||||||
which is not ``None``, the match fails if the attribute name is in
|
the match fails if the attribute name is in
|
||||||
``__match_args__``, else the match raises ``ImpossibleMatchError``.
|
``__match_args__``, else the match raises ``ImpossibleMatchError``.
|
||||||
|
|
||||||
Such a protocol favors simplicity of implementation over flexibility and
|
Such a protocol favors simplicity of implementation over flexibility and
|
||||||
performance. For other considered alternatives, see `rejected ideas`_.
|
performance. For other considered alternatives, see `rejected ideas`_.
|
||||||
|
|
||||||
|
For the most commonly-matched built-in types (specifically: ``bool``,
|
||||||
|
``bytearray``, ``bytes``, ``complex``, ``dict``, ``float``,
|
||||||
|
``frozenset``, ``int``, ``list``, ``set``, ``str``, ``tuple``, and
|
||||||
|
``type``), a single positional sub-pattern is allowed to be passed to
|
||||||
|
the call. Rather than being matched against any particular attribute
|
||||||
|
on the proxy, it is instead matched against the proxy itself. This
|
||||||
|
creates behavior that is useful and intuitive for these objects:
|
||||||
|
|
||||||
|
* ``bool(False)`` matches ``False`` (but not ``0``).
|
||||||
|
* ``tuple((0, 1, 2))`` matches ``(0, 1, 2)`` (but not ``[0, 1, 2]``).
|
||||||
|
* ``int(i)`` matches any ``int`` and binds it to the name ``i``.
|
||||||
|
|
||||||
|
|
||||||
Result value of ``__match__()``
|
Result value of ``__match__()``
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
@ -660,7 +661,7 @@ Special attribute ``__match_args__``
|
||||||
|
|
||||||
The ``__match_args__`` attribute complements the ``__match__`` method and is
|
The ``__match_args__`` attribute complements the ``__match__`` method and is
|
||||||
always looked up on the same class as the ``__match__`` method.
|
always looked up on the same class as the ``__match__`` method.
|
||||||
``__match_args__``, if it is present and not ``None``, must be a list or
|
``__match_args__``, if it is present, must be a list or
|
||||||
tuple of strings naming the allowed positional arguments.
|
tuple of strings naming the allowed positional arguments.
|
||||||
|
|
||||||
|
|
||||||
|
@ -682,11 +683,11 @@ a class wants to disallow pattern matching against itself, it should define
|
||||||
``__match__ = None``. This will cause an exception when trying to match
|
``__match__ = None``. This will cause an exception when trying to match
|
||||||
against such a class.
|
against such a class.
|
||||||
|
|
||||||
The above implementation means that by default only match-by-name and a single
|
The above implementation means that by default only match-by-name will
|
||||||
positional match by value against the proxy will work,
|
work,
|
||||||
and classes should define ``__match_args__`` (e.g. as a class
|
and classes should define ``__match_args__`` (e.g. as a class
|
||||||
attribute) if they would like to support match-by-position. Additionally,
|
attribute) if they would like to support match-by-position. Additionally,
|
||||||
dataclasses will support match-by-position out of the box. See below for more
|
dataclasses and named tuples will support match-by-position out of the box. See below for more
|
||||||
details.
|
details.
|
||||||
|
|
||||||
Finally, all attributes are exposed for matching, if a class wants to hide
|
Finally, all attributes are exposed for matching, if a class wants to hide
|
||||||
|
|
Loading…
Reference in New Issue