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:
Daniel F Moisset 2021-03-01 04:08:38 +00:00 committed by GitHub
parent d20279494a
commit a22bca6b1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 821 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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