mirror of
https://github.com/python/cpython.git
synced 2025-11-03 03:22:27 +00:00
bpo-42128: Add documentation for pattern matching (PEP 634) (#24664)
This is a first edition, ready to go out with the implementation. We'll iterate during the rest of the period leading up to 3.10.0. Co-authored-by: Carol Willing <carolcode@willingconsulting.com> Co-authored-by: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Co-authored-by: Brandt Bucher <brandt@python.org> Co-authored-by: Raymond Hettinger <1623689+rhettinger@users.noreply.github.com> Co-authored-by: Guido van Rossum <guido@python.org>
This commit is contained in:
parent
d20279494a
commit
a22bca6b1e
5 changed files with 821 additions and 3 deletions
|
|
@ -259,9 +259,8 @@ Why isn't there a switch or case statement in Python?
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
|
|
||||||
You can do this easily enough with a sequence of ``if... elif... elif... else``.
|
You can do this easily enough with a sequence of ``if... elif... elif... else``.
|
||||||
There have been some proposals for switch statement syntax, but there is no
|
For literal values, or constants within a namespace, you can also use a
|
||||||
consensus (yet) on whether and how to do range tests. See :pep:`275` for
|
``match ... case`` statement.
|
||||||
complete details and the current status.
|
|
||||||
|
|
||||||
For cases where you need to choose from a very large number of possibilities,
|
For cases where you need to choose from a very large number of possibilities,
|
||||||
you can create a dictionary mapping case values to functions to call. For
|
you can create a dictionary mapping case values to functions to call. For
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ Summarizing:
|
||||||
: | `for_stmt`
|
: | `for_stmt`
|
||||||
: | `try_stmt`
|
: | `try_stmt`
|
||||||
: | `with_stmt`
|
: | `with_stmt`
|
||||||
|
: | `match_stmt`
|
||||||
: | `funcdef`
|
: | `funcdef`
|
||||||
: | `classdef`
|
: | `classdef`
|
||||||
: | `async_with_stmt`
|
: | `async_with_stmt`
|
||||||
|
|
@ -510,6 +511,602 @@ the items are surrounded by parentheses. For example::
|
||||||
The specification, background, and examples for the Python :keyword:`with`
|
The specification, background, and examples for the Python :keyword:`with`
|
||||||
statement.
|
statement.
|
||||||
|
|
||||||
|
.. _match:
|
||||||
|
|
||||||
|
The :keyword:`!match` statement
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
! statement: match
|
||||||
|
! keyword: case
|
||||||
|
! single: pattern matching
|
||||||
|
keyword: if
|
||||||
|
keyword: as
|
||||||
|
pair: match; case
|
||||||
|
single: : (colon); compound statement
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
The match statement is used for pattern matching. Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
match_stmt: 'match' `subject_expr` ":" NEWLINE INDENT `case_block`+ DEDENT
|
||||||
|
subject_expr: `star_named_expression` "," `star_named_expressions`?
|
||||||
|
: | `named_expression`
|
||||||
|
case_block: 'case' `patterns` [`guard`] ':' `block`
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This section uses single quotes to denote
|
||||||
|
:ref:`soft keywords <soft-keywords>`.
|
||||||
|
|
||||||
|
Pattern matching takes a pattern as input (following ``case``) and a subject
|
||||||
|
value (following ``match``). The pattern (which may contain subpatterns) is
|
||||||
|
matched against the subject value. The outcomes are:
|
||||||
|
|
||||||
|
* A match success or failure (also termed a pattern success or failure).
|
||||||
|
|
||||||
|
* Possible binding of matched values to a name. The prerequisites for this are
|
||||||
|
further discussed below.
|
||||||
|
|
||||||
|
The ``match`` and ``case`` keywords are :ref:`soft keywords <soft-keywords>`.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
* :pep:`634` -- Structural Pattern Matching: Specification
|
||||||
|
* :pep:`636` -- Structural Pattern Matching: Tutorial
|
||||||
|
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
Here's an overview of the logical flow of a match statement:
|
||||||
|
|
||||||
|
|
||||||
|
#. The subject expression ``subject_expr`` is evaluated and a resulting subject
|
||||||
|
value obtained. If the subject expression contains a comma, a tuple is
|
||||||
|
constructed using :ref:`the standard rules <typesseq-tuple>`.
|
||||||
|
|
||||||
|
#. Each pattern in a ``case_block`` is attempted to match with the subject value. The
|
||||||
|
specific rules for success or failure are described below. The match attempt can also
|
||||||
|
bind some or all of the standalone names within the pattern. The precise
|
||||||
|
pattern binding rules vary per pattern type and are
|
||||||
|
specified below. **Name bindings made during a successful pattern match
|
||||||
|
outlive the executed block and can be used after the match statement**.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
During failed pattern matches, some subpatterns may succeed. Do not
|
||||||
|
rely on bindings being made for a failed match. Conversely, do not
|
||||||
|
rely on variables remaining unchanged after a failed match. The exact
|
||||||
|
behavior is dependent on implementation and may vary. This is an
|
||||||
|
intentional decision made to allow different implementations to add
|
||||||
|
optimizations.
|
||||||
|
|
||||||
|
#. If the pattern succeeds, the corresponding guard (if present) is evaluated. In
|
||||||
|
this case all name bindings are guaranteed to have happened.
|
||||||
|
|
||||||
|
* If the guard evaluates as truthy or missing, the ``block`` inside ``case_block`` is
|
||||||
|
executed.
|
||||||
|
|
||||||
|
* Otherwise, the next ``case_block`` is attempted as described above.
|
||||||
|
|
||||||
|
* If there are no further case blocks, the match statement is completed.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Users should generally never rely on a pattern being evaluated. Depending on
|
||||||
|
implementation, the interpreter may cache values or use other optimizations
|
||||||
|
which skip repeated evaluations.
|
||||||
|
|
||||||
|
A sample match statement::
|
||||||
|
|
||||||
|
>>> flag = False
|
||||||
|
>>> match (100, 200):
|
||||||
|
... case (100, 300): # Mismatch: 200 != 300
|
||||||
|
... print('Case 1')
|
||||||
|
... case (100, 200) if flag: # Successful match, but guard fails
|
||||||
|
... print('Case 2')
|
||||||
|
... case (100, y): # Matches and binds y to 200
|
||||||
|
... print(f'Case 3, y: {y}')
|
||||||
|
... case _: # Pattern not attempted
|
||||||
|
... print('Case 4, I match anything!')
|
||||||
|
...
|
||||||
|
Case 3, y: 200
|
||||||
|
|
||||||
|
|
||||||
|
In this case, ``if flag`` is a guard. Read more about that in the next section.
|
||||||
|
|
||||||
|
Guards
|
||||||
|
------
|
||||||
|
|
||||||
|
.. index:: ! guard
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
guard: "if" `named_expression`
|
||||||
|
|
||||||
|
A ``guard`` (which is part of the ``case``) must succeed for code inside
|
||||||
|
the ``case`` block to execute. It takes the form: :keyword:`if` followed by an
|
||||||
|
expression.
|
||||||
|
|
||||||
|
|
||||||
|
The logical flow of a ``case`` block with a ``guard`` follows:
|
||||||
|
|
||||||
|
#. Check that the pattern in the ``case`` block succeeded. If the pattern
|
||||||
|
failed, the ``guard`` is not evaluated and the next ``case`` block is
|
||||||
|
checked.
|
||||||
|
|
||||||
|
#. If the pattern succeeded, evaluate the ``guard``.
|
||||||
|
|
||||||
|
* If the ``guard`` condition evaluates to "truthy", the case block is
|
||||||
|
selected.
|
||||||
|
|
||||||
|
* If the ``guard`` condition evaluates to "falsy", the case block is not
|
||||||
|
selected.
|
||||||
|
|
||||||
|
* If the ``guard`` raises an exception during evaluation, the exception
|
||||||
|
bubbles up.
|
||||||
|
|
||||||
|
Guards are allowed to have side effects as they are expressions. Guard
|
||||||
|
evaluation must proceed from the first to the last case block, one at a time,
|
||||||
|
skipping case blocks whose pattern(s) don't all succeed. (I.e.,
|
||||||
|
guard evaluation must happen in order.) Guard evaluation must stop once a case
|
||||||
|
block is selected.
|
||||||
|
|
||||||
|
|
||||||
|
.. _irrefutable_case:
|
||||||
|
|
||||||
|
Irrefutable Case Blocks
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. index:: irrefutable case block, case block
|
||||||
|
|
||||||
|
An irrefutable case block is a match-all case block. A match statement may have
|
||||||
|
at most one irrefutable case block, and it must be last.
|
||||||
|
|
||||||
|
A case block is considered irrefutable if it has no guard and its pattern is
|
||||||
|
irrefutable. A pattern is considered irrefutable if we can prove from its
|
||||||
|
syntax alone that it will always succeed. Only the following patterns are
|
||||||
|
irrefutable:
|
||||||
|
|
||||||
|
* :ref:`as-patterns` whose left-hand side is irrefutable
|
||||||
|
|
||||||
|
* :ref:`or-patterns` containing at least one irrefutable pattern
|
||||||
|
|
||||||
|
* :ref:`capture-patterns`
|
||||||
|
|
||||||
|
* :ref:`wildcard-patterns`
|
||||||
|
|
||||||
|
* parenthesized irrefutable patterns
|
||||||
|
|
||||||
|
|
||||||
|
Patterns
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. index::
|
||||||
|
single: ! patterns
|
||||||
|
single: AS pattern, OR pattern, capture pattern, wildcard pattern
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This section uses grammar notations beyond standard EBNF:
|
||||||
|
|
||||||
|
* the notation ``SEP.RULE+`` is shorthand for ``RULE (SEP RULE)*``
|
||||||
|
|
||||||
|
* the notation ``!RULE`` is shorthand for a negative lookahead assertion
|
||||||
|
|
||||||
|
|
||||||
|
The top-level syntax for ``patterns`` is:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
patterns: `open_sequence_pattern` | `pattern`
|
||||||
|
pattern: `as_pattern` | `or_pattern`
|
||||||
|
closed_pattern: | `literal_pattern`
|
||||||
|
: | `capture_pattern`
|
||||||
|
: | `wildcard_pattern`
|
||||||
|
: | `value_pattern`
|
||||||
|
: | `group_pattern`
|
||||||
|
: | `sequence_pattern`
|
||||||
|
: | `mapping_pattern`
|
||||||
|
: | `class_pattern`
|
||||||
|
|
||||||
|
The descriptions below will include a description "in simple terms" of what a pattern
|
||||||
|
does for illustration purposes (credits to Raymond Hettinger for a document that
|
||||||
|
inspired most of the descriptions). Note that these descriptions are purely for
|
||||||
|
illustration purposes and **may not** reflect
|
||||||
|
the underlying implementation. Furthermore, they do not cover all valid forms.
|
||||||
|
|
||||||
|
|
||||||
|
.. _or-patterns:
|
||||||
|
|
||||||
|
OR Patterns
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
An OR pattern is two or more patterns separated by vertical
|
||||||
|
bars ``|``. Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
or_pattern: "|".`closed_pattern`+
|
||||||
|
|
||||||
|
Only the final subpattern may be :ref:`irrefutable <irrefutable_case>`, and each
|
||||||
|
subpattern must bind the same set of names to avoid ambiguity.
|
||||||
|
|
||||||
|
An OR pattern matches each of its subpatterns in turn to the subject value,
|
||||||
|
until one succeeds. The OR pattern is then considered successful. Otherwise,
|
||||||
|
if none of the subpatterns succeed, the OR pattern fails.
|
||||||
|
|
||||||
|
In simple terms, ``P1 | P2 | ...`` will try to match ``P1``, if it fails it will try to
|
||||||
|
match ``P2``, succeeding immediately if any succeeds, failing otherwise.
|
||||||
|
|
||||||
|
.. _as-patterns:
|
||||||
|
|
||||||
|
AS Patterns
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
An AS pattern matches an OR pattern on the left of the :keyword:`as`
|
||||||
|
keyword against a subject. Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
as_pattern: `or_pattern` "as" `capture_pattern`
|
||||||
|
|
||||||
|
If the OR pattern fails, the AS pattern fails. Otherwise, the AS pattern binds
|
||||||
|
the subject to the name on the right of the as keyword and succeeds.
|
||||||
|
``capture_pattern`` cannot be a a ``_``.
|
||||||
|
|
||||||
|
In simple terms ``P as NAME`` will match with ``P``, and on success it will
|
||||||
|
set ``NAME = <subject>``.
|
||||||
|
|
||||||
|
|
||||||
|
.. _literal-patterns:
|
||||||
|
|
||||||
|
Literal Patterns
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A literal pattern corresponds to most
|
||||||
|
:ref:`literals <literals>` in Python. Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
literal_pattern: `signed_number`
|
||||||
|
: | `signed_number` "+" NUMBER
|
||||||
|
: | `signed_number` "-" NUMBER
|
||||||
|
: | `strings`
|
||||||
|
: | "None"
|
||||||
|
: | "True"
|
||||||
|
: | "False"
|
||||||
|
: | `signed_number`: NUMBER | "-" NUMBER
|
||||||
|
|
||||||
|
The rule ``strings`` and the token ``NUMBER`` are defined in the
|
||||||
|
:doc:`standard Python grammar <./grammar>`. Triple-quoted strings are
|
||||||
|
supported. Raw strings and byte strings are supported. :ref:`f-strings` are
|
||||||
|
not supported.
|
||||||
|
|
||||||
|
The forms ``signed_number '+' NUMBER`` and ``signed_number '-' NUMBER`` are
|
||||||
|
for expressing :ref:`complex numbers <imaginary>`; they require a real number
|
||||||
|
on the left and an imaginary number on the right. E.g. ``3 + 4j``.
|
||||||
|
|
||||||
|
In simple terms, ``LITERAL`` will succeed only if ``<subject> == LITERAL``. For
|
||||||
|
the singletons ``None``, ``True`` and ``False``, the :keyword:`is` operator is used.
|
||||||
|
|
||||||
|
.. _capture-patterns:
|
||||||
|
|
||||||
|
Capture Patterns
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A capture pattern binds the subject value to a name.
|
||||||
|
Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
capture_pattern: !'_' NAME
|
||||||
|
|
||||||
|
A single underscore ``_`` is not a capture pattern (this is what ``!'_'``
|
||||||
|
expresses). And is instead treated as a :token:`wildcard_pattern`.
|
||||||
|
|
||||||
|
In a given pattern, a given name can only be bound once. E.g.
|
||||||
|
``case x, x: ...`` is invalid while ``case [x] | x: ...`` is allowed.
|
||||||
|
|
||||||
|
Capture patterns always succeed. The binding follows scoping rules
|
||||||
|
established by the assignment expression operator in :pep:`572`; the
|
||||||
|
name becomes a local variable in the closest containing function scope unless
|
||||||
|
there's an applicable :keyword:`global` or :keyword:`nonlocal` statement.
|
||||||
|
|
||||||
|
In simple terms ``NAME`` will always succeed and it will set ``NAME = <subject>``.
|
||||||
|
|
||||||
|
.. _wildcard-patterns:
|
||||||
|
|
||||||
|
Wildcard Patterns
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A wildcard pattern always succeeds (matches anything)
|
||||||
|
and binds no name. Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
wildcard_pattern: '_'
|
||||||
|
|
||||||
|
``_`` is a :ref:`soft keyword <soft-keywords>`.
|
||||||
|
|
||||||
|
In simple terms, ``_`` will always succeed.
|
||||||
|
|
||||||
|
.. _value-patterns:
|
||||||
|
|
||||||
|
Value Patterns
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A value pattern represents a named value in Python.
|
||||||
|
Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
value_pattern: `attr`
|
||||||
|
attr: `name_or_attr` "." NAME
|
||||||
|
name_or_attr: `attr` | NAME
|
||||||
|
|
||||||
|
The dotted name in the pattern is looked up using standard Python
|
||||||
|
:ref:`name resolution rules <resolve_names>`. The pattern succeeds if the
|
||||||
|
value found compares equal to the subject value (using the ``==`` equality
|
||||||
|
operator).
|
||||||
|
|
||||||
|
In simple terms ``NAME1.NAME2`` will succeed only if ``<subject> == NAME1.NAME2``
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If the same value occurs multiple times in the same match statement, the
|
||||||
|
interpreter may cache the first value found and reuse it rather than repeat
|
||||||
|
the same lookup. This cache is strictly tied to a given execution of a
|
||||||
|
given match statement.
|
||||||
|
|
||||||
|
.. _group-patterns:
|
||||||
|
|
||||||
|
Group Patterns
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A group pattern allows users to add parentheses around patterns to
|
||||||
|
emphasize the intended grouping. Otherwise, it has no additional syntax.
|
||||||
|
Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
group_pattern: '(' `pattern` ')'
|
||||||
|
|
||||||
|
In simple terms ``(P)`` has the same effect as ``P``.
|
||||||
|
|
||||||
|
.. _sequence-patterns:
|
||||||
|
|
||||||
|
Sequence Patterns
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A sequence pattern contains several subpatterns to be matched against sequence elements.
|
||||||
|
The syntax is similar to the unpacking of a list or tuple.
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
sequence_pattern: "[" [`maybe_sequence_pattern`] "]"
|
||||||
|
: | "(" [`open_sequence_pattern`] ")"
|
||||||
|
open_sequence_pattern: `maybe_star_pattern` "," [`maybe_sequence_pattern`]
|
||||||
|
maybe_sequence_pattern: ",".`maybe_star_pattern`+ ","?
|
||||||
|
maybe_star_pattern: `star_pattern` | `pattern`
|
||||||
|
star_pattern: "*" (`capture_pattern` | `wildcard_pattern`)
|
||||||
|
|
||||||
|
There is no difference if parentheses or square brackets
|
||||||
|
are used for sequence patterns (i.e. ``(...)`` vs ``[...]`` ).
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
A single pattern enclosed in parentheses without a trailing comma
|
||||||
|
(e.g. ``(3 | 4)``) is a :ref:`group pattern <group-patterns>`.
|
||||||
|
While a single pattern enclosed in square brackets (e.g. ``[3 | 4]``) is
|
||||||
|
still a sequence pattern.
|
||||||
|
|
||||||
|
At most one star subpattern may be in a sequence pattern. The star subpattern
|
||||||
|
may occur in any position. If no star subpattern is present, the sequence
|
||||||
|
pattern is a fixed-length sequence pattern; otherwise it is a variable-length
|
||||||
|
sequence pattern.
|
||||||
|
|
||||||
|
The following is the logical flow for matching a sequence pattern against a
|
||||||
|
subject value:
|
||||||
|
|
||||||
|
#. If the subject value is not an instance of a
|
||||||
|
:class:`collections.abc.Sequence` the sequence pattern fails.
|
||||||
|
|
||||||
|
#. If the subject value is an instance of ``str``, ``bytes`` or ``bytearray``
|
||||||
|
the sequence pattern fails.
|
||||||
|
|
||||||
|
#. The subsequent steps depend on whether the sequence pattern is fixed or
|
||||||
|
variable-length.
|
||||||
|
|
||||||
|
If the sequence pattern is fixed-length:
|
||||||
|
|
||||||
|
#. If the length of the subject sequence is not equal to the number of
|
||||||
|
subpatterns, the sequence pattern fails
|
||||||
|
|
||||||
|
#. Subpatterns in the sequence pattern are matched to their corresponding
|
||||||
|
items in the subject sequence from left to right. Matching stops as soon
|
||||||
|
as a subpattern fails. If all subpatterns succeed in matching their
|
||||||
|
corresponding item, the sequence pattern succeeds.
|
||||||
|
|
||||||
|
Otherwise, if the sequence pattern is variable-length:
|
||||||
|
|
||||||
|
#. If the length of the subject sequence is less than the number of non-star
|
||||||
|
subpatterns, the sequence pattern fails.
|
||||||
|
|
||||||
|
#. The leading non-star subpatterns are matched to their corresponding items
|
||||||
|
as for fixed-length sequences.
|
||||||
|
|
||||||
|
#. If the previous step succeeds, the star subpattern matches a list formed
|
||||||
|
of the remaining subject items, excluding the remaining items
|
||||||
|
corresponding to non-star subpatterns following the star subpattern.
|
||||||
|
|
||||||
|
#. Remaining non-star subpatterns are matched to their corresponding subject
|
||||||
|
items, as for a fixed-length sequence.
|
||||||
|
|
||||||
|
.. note:: The length of the subject sequence is obtained via
|
||||||
|
:func:`len` (i.e. via the :meth:`__len__` protocol). This length may be
|
||||||
|
cached by the interpreter in a similar manner as
|
||||||
|
:ref:`value patterns <value-patterns>`.
|
||||||
|
|
||||||
|
|
||||||
|
In simple terms ``[P1, P2, P3,`` ... ``, P<N>]`` matches only if all the following
|
||||||
|
happens:
|
||||||
|
|
||||||
|
* ``isinstance(<subject>, collections.abc.Sequence)``
|
||||||
|
* ``len(subject) == <N>``
|
||||||
|
* ``P1`` matches ``<subject>[0]`` (note that this match can also bind names)
|
||||||
|
* ``P2`` matches ``<subject>[1]`` (note that this match can also bind names)
|
||||||
|
* ... and so on for the corresponding pattern/element.
|
||||||
|
|
||||||
|
.. _mapping-patterns:
|
||||||
|
|
||||||
|
Mapping Patterns
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A mapping pattern contains one or more key-value patterns. The syntax is
|
||||||
|
similar to the construction of a dictionary.
|
||||||
|
Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
mapping_pattern: "{" [`items_pattern`] "}"
|
||||||
|
items_pattern: ",".`key_value_pattern`+ ","?
|
||||||
|
key_value_pattern: (`literal_pattern` | `value_pattern`) ":" `pattern`
|
||||||
|
: | `double_star_pattern`
|
||||||
|
double_star_pattern: "**" `capture_pattern`
|
||||||
|
|
||||||
|
At most one double star pattern may be in a mapping pattern. The double star
|
||||||
|
pattern must be the last subpattern in the mapping pattern.
|
||||||
|
|
||||||
|
Duplicate key values in mapping patterns are disallowed. (If all key patterns
|
||||||
|
are literal patterns this is considered a syntax error; otherwise this is a
|
||||||
|
runtime error and will raise :exc:`ValueError`.)
|
||||||
|
|
||||||
|
The following is the logical flow for matching a mapping pattern against a
|
||||||
|
subject value:
|
||||||
|
|
||||||
|
#. If the subject value is not an instance of :class:`collections.abc.Mapping`,
|
||||||
|
the mapping pattern fails.
|
||||||
|
|
||||||
|
#. If every key given in the mapping pattern is present in the subject mapping,
|
||||||
|
and the pattern for each key matches the corresponding item of the subject
|
||||||
|
mapping, the mapping pattern succeeds.
|
||||||
|
|
||||||
|
#. If duplicate keys are detected in the mapping pattern, the pattern is
|
||||||
|
considered invalid and :exc:`ValueError` is raised.
|
||||||
|
|
||||||
|
.. note:: Key-value pairs are matched using the two-argument form of the mapping
|
||||||
|
subject's ``get()`` method. Matched key-value pairs must already be present
|
||||||
|
in the mapping, and not created on-the-fly via :meth:`__missing__` or
|
||||||
|
:meth:`__getitem__`.
|
||||||
|
|
||||||
|
In simple terms ``{KEY1: P1, KEY2: P2, ... }`` matches only if all the following
|
||||||
|
happens:
|
||||||
|
|
||||||
|
* ``isinstance(<subject>, collections.abc.Mapping)``
|
||||||
|
* ``KEY1 in <subject>``
|
||||||
|
* ``P1`` matches ``<subject>[KEY1]``
|
||||||
|
* ... and so on for the corresponding KEY/pattern pair.
|
||||||
|
|
||||||
|
|
||||||
|
.. _class-patterns:
|
||||||
|
|
||||||
|
Class Patterns
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A class pattern represents a class and its positional and keyword arguments
|
||||||
|
(if any). Syntax:
|
||||||
|
|
||||||
|
.. productionlist:: python-grammar
|
||||||
|
class_pattern: `name_or_attr` "(" [`pattern_arguments` ","?] ")"
|
||||||
|
pattern_arguments: `positional_patterns` ["," `keyword_patterns`]
|
||||||
|
: | `keyword_patterns`
|
||||||
|
positional_patterns: ",".`pattern`+
|
||||||
|
keyword_patterns: ",".`keyword_pattern`+
|
||||||
|
keyword_pattern: NAME "=" `pattern`
|
||||||
|
|
||||||
|
The same keyword should not be repeated in class patterns.
|
||||||
|
|
||||||
|
The following is the logical flow for matching a mapping pattern against a
|
||||||
|
subject value:
|
||||||
|
|
||||||
|
#. If ``name_or_attr`` is not an instance of the builtin :class:`type` , raise
|
||||||
|
:exc:`TypeError`.
|
||||||
|
|
||||||
|
#. If the subject value is not an instance of ``name_or_attr`` (tested via
|
||||||
|
:func:`isinstance`), the class pattern fails.
|
||||||
|
|
||||||
|
#. If no pattern arguments are present, the pattern succeeds. Otherwise,
|
||||||
|
the subsequent steps depend on whether keyword or positional argument patterns
|
||||||
|
are present.
|
||||||
|
|
||||||
|
For a number of built-in types (specified below), a single positional
|
||||||
|
subpattern is accepted which will match the entire subject; for these types
|
||||||
|
no keyword patterns are accepted.
|
||||||
|
|
||||||
|
If only keyword patterns are present, they are processed as follows,
|
||||||
|
one by one:
|
||||||
|
|
||||||
|
I. The keyword is looked up as an attribute on the subject.
|
||||||
|
|
||||||
|
* If this raises an exception other than :exc:`AttributeError`, the
|
||||||
|
exception bubbles up.
|
||||||
|
|
||||||
|
* If this raises :exc:`AttributeError`, the class pattern has failed.
|
||||||
|
|
||||||
|
* Else, the subpattern associated with the keyword pattern is matched
|
||||||
|
against the subject's attribute value. If this fails, the class
|
||||||
|
pattern fails; if this succeeds, the match proceeds to the next keyword.
|
||||||
|
|
||||||
|
|
||||||
|
II. If all keyword patterns succeed, the class pattern succeeds.
|
||||||
|
|
||||||
|
If any positional patterns are present, they are converted to keyword
|
||||||
|
patterns using the :data:`~object.__match_args__` attribute on the class
|
||||||
|
``name_or_attr`` before matching:
|
||||||
|
|
||||||
|
I. The equivalent of ``getattr(cls, "__match_args__", ()))`` is called.
|
||||||
|
|
||||||
|
* If this raises an exception, the exception bubbles up.
|
||||||
|
|
||||||
|
* If the returned value is not a list or tuple, the conversion fails and
|
||||||
|
:exc:`TypeError` is raised.
|
||||||
|
|
||||||
|
* If there are more positional patterns than ``len(cls.__match_args__)``,
|
||||||
|
:exc:`TypeError` is raised.
|
||||||
|
|
||||||
|
* Otherwise, positional pattern ``i`` is converted to a keyword pattern
|
||||||
|
using ``__match_args__[i]`` as the keyword. ``__match_args__[i]`` must
|
||||||
|
be a string; if not :exc:`TypeError` is raised.
|
||||||
|
|
||||||
|
* If there are duplicate keywords, :exc:`TypeError` is raised.
|
||||||
|
|
||||||
|
.. seealso:: :ref:`class-pattern-matching`
|
||||||
|
|
||||||
|
II. Once all positional patterns have been converted to keyword patterns,
|
||||||
|
the match proceeds as if there were only keyword patterns.
|
||||||
|
|
||||||
|
For the following built-in types the handling of positional subpatterns is
|
||||||
|
different:
|
||||||
|
|
||||||
|
* :class:`bool`
|
||||||
|
* :class:`bytearray`
|
||||||
|
* :class:`bytes`
|
||||||
|
* :class:`dict`
|
||||||
|
* :class:`float`
|
||||||
|
* :class:`frozenset`
|
||||||
|
* :class:`int`
|
||||||
|
* :class:`list`
|
||||||
|
* :class:`set`
|
||||||
|
* :class:`str`
|
||||||
|
* :class:`tuple`
|
||||||
|
|
||||||
|
These classes accept a single positional argument, and the pattern there is matched
|
||||||
|
against the whole object rather than an attribute. For example ``int(0|1)`` matches
|
||||||
|
the value ``0``, but not the values ``0.0`` or ``False``.
|
||||||
|
|
||||||
|
In simple terms ``CLS(P1, attr=P2)`` matches only if the following happens:
|
||||||
|
|
||||||
|
* ``isinstance(<subject>, CLS)``
|
||||||
|
* convert ``P1`` to a keyword pattern using ``CLS.__match_args__``
|
||||||
|
* For each keyword argument ``attr=P2``:
|
||||||
|
* ``hasattr(<subject>, "attr")``
|
||||||
|
* ``P2`` matches ``<subject>.attr``
|
||||||
|
* ... and so on for the corresponding keyword argument/pattern pair.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
* :pep:`634` -- Structural Pattern Matching: Specification
|
||||||
|
* :pep:`636` -- Structural Pattern Matching: Tutorial
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: parameter; function definition
|
single: parameter; function definition
|
||||||
|
|
|
||||||
|
|
@ -2553,6 +2553,38 @@ For more information on context managers, see :ref:`typecontextmanager`.
|
||||||
statement.
|
statement.
|
||||||
|
|
||||||
|
|
||||||
|
.. _class-pattern-matching:
|
||||||
|
|
||||||
|
Customizing positional arguments in class pattern matching
|
||||||
|
----------------------------------------------------------
|
||||||
|
|
||||||
|
When using a class name in a pattern, positional arguments in the pattern are not
|
||||||
|
allowed by default, i.e. ``case MyClass(x, y)`` is typically invalid without special
|
||||||
|
support in ``MyClass``. To be able to use that kind of patterns, the class needs to
|
||||||
|
define a *__match_args__* attribute.
|
||||||
|
|
||||||
|
.. data:: object.__match_args__
|
||||||
|
|
||||||
|
This class variable can be assigned a tuple or list of strings. When this class is
|
||||||
|
used in a class pattern with positional arguments, each positional argument will
|
||||||
|
be converted into a keyword argument, using the corresponding value in
|
||||||
|
*__match_args__* as the keyword. The absence of this attribute is equivalent to
|
||||||
|
setting it to ``()``.
|
||||||
|
|
||||||
|
For example, if ``MyClass.__match_args__`` is ``("left", "center", "right")`` that means
|
||||||
|
that ``case MyClass(x, y)`` is equivalent to ``case MyClass(left=x, center=y)``. Note
|
||||||
|
that the number of arguments in the pattern must be smaller than or equal to the number
|
||||||
|
of elements in *__match_args__*; if it is larger, the pattern match attempt will raise
|
||||||
|
a :exc:`TypeError`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
:pep:`634` - Structural Pattern Matching
|
||||||
|
The specification for the Python ``match`` statement.
|
||||||
|
|
||||||
|
|
||||||
.. _special-lookup:
|
.. _special-lookup:
|
||||||
|
|
||||||
Special method lookup
|
Special method lookup
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,27 @@ exactly as written here:
|
||||||
assert del global not with
|
assert del global not with
|
||||||
async elif if or yield
|
async elif if or yield
|
||||||
|
|
||||||
|
|
||||||
|
.. _soft-keywords:
|
||||||
|
|
||||||
|
Soft Keywords
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. index:: soft keyword, keyword
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
Some identifiers are only reserved under specific contexts. These are known as
|
||||||
|
*soft keywords*. The identifiers ``match``, ``case`` and ``_`` can
|
||||||
|
syntactically act as keywords in contexts related to the pattern matching
|
||||||
|
statement, but this distinction is done at the parser level, not when
|
||||||
|
tokenizing.
|
||||||
|
|
||||||
|
As soft keywords, their use with pattern matching is possible while still
|
||||||
|
preserving compatibility with existing code that uses ``match``, ``case`` and ``_`` as
|
||||||
|
identifier names.
|
||||||
|
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: _, identifiers
|
single: _, identifiers
|
||||||
single: __, identifiers
|
single: __, identifiers
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ to avoid excessive indentation. An :keyword:`!if` ... :keyword:`!elif` ...
|
||||||
:keyword:`!elif` ... sequence is a substitute for the ``switch`` or
|
:keyword:`!elif` ... sequence is a substitute for the ``switch`` or
|
||||||
``case`` statements found in other languages.
|
``case`` statements found in other languages.
|
||||||
|
|
||||||
|
If you're comparing the same value to several constants, or checking for specific types or
|
||||||
|
attributes, you may also find the :keyword:`!match` statement useful. For more
|
||||||
|
details see :ref:`tut-match`.
|
||||||
|
|
||||||
.. _tut-for:
|
.. _tut-for:
|
||||||
|
|
||||||
|
|
@ -246,6 +249,172 @@ at a more abstract level. The :keyword:`!pass` is silently ignored::
|
||||||
... pass # Remember to implement this!
|
... pass # Remember to implement this!
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
.. _tut-match:
|
||||||
|
|
||||||
|
:keyword:`!match` Statements
|
||||||
|
============================
|
||||||
|
|
||||||
|
A match statement takes an expression and compares its value to successive
|
||||||
|
patterns given as one or more case blocks. This is superficially
|
||||||
|
similar to a switch statement in C, Java or JavaScript (and many
|
||||||
|
other languages), but it can also extract components (sequence elements or
|
||||||
|
object attributes) from the value into variables.
|
||||||
|
|
||||||
|
The simplest form compares a subject value against one or more literals::
|
||||||
|
|
||||||
|
def http_error(status):
|
||||||
|
match status:
|
||||||
|
case 400:
|
||||||
|
return "Bad request"
|
||||||
|
case 404:
|
||||||
|
return "Not found"
|
||||||
|
case 418:
|
||||||
|
return "I'm a teapot"
|
||||||
|
case _:
|
||||||
|
return "Something's wrong with the Internet"
|
||||||
|
|
||||||
|
Note the last block: the "variable name" ``_`` acts as a *wildcard* and
|
||||||
|
never fails to match. If no case matches, none of the branches is executed.
|
||||||
|
|
||||||
|
You can combine several literals in a single pattern using ``|`` ("or")::
|
||||||
|
|
||||||
|
case 401 | 403 | 404:
|
||||||
|
return "Not allowed"
|
||||||
|
|
||||||
|
Patterns can look like unpacking assignments, and can be used to bind
|
||||||
|
variables::
|
||||||
|
|
||||||
|
# point is an (x, y) tuple
|
||||||
|
match point:
|
||||||
|
case (0, 0):
|
||||||
|
print("Origin")
|
||||||
|
case (0, y):
|
||||||
|
print(f"Y={y}")
|
||||||
|
case (x, 0):
|
||||||
|
print(f"X={x}")
|
||||||
|
case (x, y):
|
||||||
|
print(f"X={x}, Y={y}")
|
||||||
|
case _:
|
||||||
|
raise ValueError("Not a point")
|
||||||
|
|
||||||
|
Study that one carefully! The first pattern has two literals, and can
|
||||||
|
be thought of as an extension of the literal pattern shown above. But
|
||||||
|
the next two patterns combine a literal and a variable, and the
|
||||||
|
variable *binds* a value from the subject (``point``). The fourth
|
||||||
|
pattern captures two values, which makes it conceptually similar to
|
||||||
|
the unpacking assignment ``(x, y) = point``.
|
||||||
|
|
||||||
|
If you are using classes to structure your data
|
||||||
|
you can use the class name followed by an argument list resembling a
|
||||||
|
constructor, but with the ability to capture attributes into variables::
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
|
||||||
|
def where_is(point):
|
||||||
|
match point:
|
||||||
|
case Point(x=0, y=0):
|
||||||
|
print("Origin")
|
||||||
|
case Point(x=0, y=y):
|
||||||
|
print(f"Y={y}")
|
||||||
|
case Point(x=x, y=0):
|
||||||
|
print(f"X={x}")
|
||||||
|
case Point():
|
||||||
|
print("Somewhere else")
|
||||||
|
case _:
|
||||||
|
print("Not a point")
|
||||||
|
|
||||||
|
You can use positional parameters with some builtin classes that provide an
|
||||||
|
ordering for their attributes (e.g. dataclasses). You can also define a specific
|
||||||
|
position for attributes in patterns by setting the ``__match_args__`` special
|
||||||
|
attribute in your classes. If it's set to ("x", "y"), the following patterns are all
|
||||||
|
equivalent (and all bind the ``y`` attribute to the ``var`` variable)::
|
||||||
|
|
||||||
|
Point(1, var)
|
||||||
|
Point(1, y=var)
|
||||||
|
Point(x=1, y=var)
|
||||||
|
Point(y=var, x=1)
|
||||||
|
|
||||||
|
A recommended way to read patterns is to look at them as an extended form of what you
|
||||||
|
would put on the left of an assignment, to understand which variables would be set to
|
||||||
|
what.
|
||||||
|
Only the standalone names (like ``var`` above) are assigned to by a match statement.
|
||||||
|
Dotted names (like ``foo.bar``), attribute names (the ``x=`` and ``y=`` above) or class names
|
||||||
|
(recognized by the "(...)" next to them like ``Point`` above) are never assigned to.
|
||||||
|
|
||||||
|
Patterns can be arbitrarily nested. For example, if we have a short
|
||||||
|
list of points, we could match it like this::
|
||||||
|
|
||||||
|
match points:
|
||||||
|
case []:
|
||||||
|
print("No points")
|
||||||
|
case [Point(0, 0)]:
|
||||||
|
print("The origin")
|
||||||
|
case [Point(x, y)]:
|
||||||
|
print(f"Single point {x}, {y}")
|
||||||
|
case [Point(0, y1), Point(0, y2)]:
|
||||||
|
print(f"Two on the Y axis at {y1}, {y2}")
|
||||||
|
case _:
|
||||||
|
print("Something else")
|
||||||
|
|
||||||
|
We can add an ``if`` clause to a pattern, known as a "guard". If the
|
||||||
|
guard is false, ``match`` goes on to try the next case block. Note
|
||||||
|
that value capture happens before the guard is evaluated::
|
||||||
|
|
||||||
|
match point:
|
||||||
|
case Point(x, y) if x == y:
|
||||||
|
print(f"Y=X at {x}")
|
||||||
|
case Point(x, y):
|
||||||
|
print(f"Not on the diagonal")
|
||||||
|
|
||||||
|
Several other key features of this statement:
|
||||||
|
|
||||||
|
- Like unpacking assignments, tuple and list patterns have exactly the
|
||||||
|
same meaning and actually match arbitrary sequences. An important
|
||||||
|
exception is that they don't match iterators or strings.
|
||||||
|
|
||||||
|
- Sequence patterns support extended unpacking: ``[x, y, *rest]`` and ``(x, y,
|
||||||
|
*rest)`` work similar to unpacking assignments. The
|
||||||
|
name after ``*`` may also be ``_``, so ``(x, y, *_)`` matches a sequence
|
||||||
|
of at least two items without binding the remaining items.
|
||||||
|
|
||||||
|
- Mapping patterns: ``{"bandwidth": b, "latency": l}`` captures the
|
||||||
|
``"bandwidth"`` and ``"latency"`` values from a dictionary. Unlike sequence
|
||||||
|
patterns, extra keys are ignored. An unpacking like ``**rest`` is also
|
||||||
|
supported. (But ``**_`` would be redundant, so it not allowed.)
|
||||||
|
|
||||||
|
- Subpatterns may be captured using the ``as`` keyword::
|
||||||
|
|
||||||
|
case (Point(x1, y1), Point(x2, y2) as p2): ...
|
||||||
|
|
||||||
|
will capture the second element of the input as ``p2`` (as long as the input is
|
||||||
|
a sequence of two points)
|
||||||
|
|
||||||
|
- Most literals are compared by equality, however the singletons ``True``,
|
||||||
|
``False`` and ``None`` are compared by identity.
|
||||||
|
|
||||||
|
- Patterns may use named constants. These must be dotted names
|
||||||
|
to prevent them from being interpreted as capture variable::
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
class Color(Enum):
|
||||||
|
RED = 0
|
||||||
|
GREEN = 1
|
||||||
|
BLUE = 2
|
||||||
|
|
||||||
|
match color:
|
||||||
|
case Color.RED:
|
||||||
|
print("I see red!")
|
||||||
|
case Color.GREEN:
|
||||||
|
print("Grass is green")
|
||||||
|
case Color.BLUE:
|
||||||
|
print("I'm feeling the blues :(")
|
||||||
|
|
||||||
|
For a more detailed explanation and additional examples, you can look into
|
||||||
|
:pep:`636` which is written in a tutorial format.
|
||||||
|
|
||||||
.. _tut-functions:
|
.. _tut-functions:
|
||||||
|
|
||||||
Defining Functions
|
Defining Functions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue