[Python-checkins] bpo-42128: Add documentation for pattern matching (PEP 634) (#24664)

gvanrossum webhook-mailer at python.org
Sun Feb 28 23:08:45 EST 2021


https://github.com/python/cpython/commit/a22bca6b1e2f3dc11a58f3e5ef5c22e26b8a2d80
commit: a22bca6b1e2f3dc11a58f3e5ef5c22e26b8a2d80
branch: master
author: Daniel F Moisset <dfmoisset at gmail.com>
committer: gvanrossum <gvanrossum at gmail.com>
date: 2021-02-28T20:08:38-08:00
summary:

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 at willingconsulting.com>
Co-authored-by: Fidget-Spinner <28750310+Fidget-Spinner at users.noreply.github.com>
Co-authored-by: Brandt Bucher <brandt at python.org>
Co-authored-by: Raymond Hettinger <1623689+rhettinger at users.noreply.github.com>
Co-authored-by: Guido van Rossum <guido at python.org>

files:
M Doc/faq/design.rst
M Doc/reference/compound_stmts.rst
M Doc/reference/datamodel.rst
M Doc/reference/lexical_analysis.rst
M Doc/tutorial/controlflow.rst

diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst
index 8cf271c302408..7fe1c6d58f58a 100644
--- a/Doc/faq/design.rst
+++ b/Doc/faq/design.rst
@@ -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``.
-There have been some proposals for switch statement syntax, but there is no
-consensus (yet) on whether and how to do range tests.  See :pep:`275` for
-complete details and the current status.
+For literal values, or constants within a namespace, you can also use a
+``match ... case`` statement.
 
 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
diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index f22af8b44a112..e13d6dd956209 100644
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -51,6 +51,7 @@ Summarizing:
                 : | `for_stmt`
                 : | `try_stmt`
                 : | `with_stmt`
+                : | `match_stmt`
                 : | `funcdef`
                 : | `classdef`
                 : | `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`
       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::
    single: parameter; function definition
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index 62f852953623c..dfe5eb6857c0f 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -2553,6 +2553,38 @@ For more information on context managers, see :ref:`typecontextmanager`.
       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 method lookup
diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst
index 77e0578f5d89b..4ad8f8be1e7dd 100644
--- a/Doc/reference/lexical_analysis.rst
+++ b/Doc/reference/lexical_analysis.rst
@@ -351,6 +351,27 @@ exactly as written here:
    assert     del        global     not        with
    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::
    single: _, identifiers
    single: __, identifiers
diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst
index 9ee18f75847e7..277e5c18562d4 100644
--- a/Doc/tutorial/controlflow.rst
+++ b/Doc/tutorial/controlflow.rst
@@ -36,6 +36,9 @@ to avoid excessive indentation.  An  :keyword:`!if` ... :keyword:`!elif` ...
 :keyword:`!elif` ... sequence is a substitute for the ``switch`` or
 ``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:
 
@@ -246,6 +249,172 @@ at a more abstract level.  The :keyword:`!pass` is silently ignored::
    ...     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:
 
 Defining Functions



More information about the Python-checkins mailing list