PEP 636: Add Appendix A -- Quick Intro (#1646)
This commit is contained in:
parent
a888e419e7
commit
1d43bca863
175
pep-0636.rst
175
pep-0636.rst
|
@ -34,6 +34,9 @@ This is considered supporting material for PEP 634 (the technical specification
|
|||
for pattern matching) and PEP 635 (the motivation and rationale for having pattern
|
||||
matching and design considerations).
|
||||
|
||||
For readers who are looking more for a quick review than for a tutorial,
|
||||
see `Appendix A`_.
|
||||
|
||||
Meta
|
||||
====
|
||||
|
||||
|
@ -368,6 +371,178 @@ example of getting constants from module (like key names for keyboard events)
|
|||
|
||||
customizing match_args?
|
||||
|
||||
|
||||
.. _Appendix A:
|
||||
|
||||
Appendix A -- Quick Intro
|
||||
=========================
|
||||
|
||||
A ``match`` statement takes an expression and compares it to successive
|
||||
patterns given as one or more ``case`` blocks. This is superficially
|
||||
similar to a ``switch`` statement in C, Java or JavaScript (an many
|
||||
other languages), but much more powerful.
|
||||
|
||||
The simplest form compares a subject value against one or more literals::
|
||||
|
||||
def http_error(status):
|
||||
match status:
|
||||
case 400:
|
||||
return "Bad request"
|
||||
case 401:
|
||||
return "Unauthorized"
|
||||
case 403:
|
||||
return "Forbidden"
|
||||
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.
|
||||
|
||||
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::
|
||||
|
||||
# The subject 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 *captures* 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 (e.g. data classes)
|
||||
you can use the class name followed by an argument list resembling a
|
||||
constructor, but with the ability to capture variables::
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
def whereis(point):
|
||||
match point:
|
||||
case Point(0, 0):
|
||||
print("Origin")
|
||||
case Point(0, y):
|
||||
print(f"Y={y}")
|
||||
case Point(x, 0):
|
||||
print(f"X={x}")
|
||||
case Point():
|
||||
print("Somewhere else")
|
||||
case _:
|
||||
print("Not a point")
|
||||
|
||||
We can use keyword parameters too. 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)
|
||||
|
||||
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:
|
||||
|
||||
- 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.
|
||||
(Technically, the subject must be an instance of
|
||||
``collections.abc.Sequence``.)
|
||||
|
||||
- Sequence patterns support wildcards: ``[x, y, *rest]`` and ``(x, y,
|
||||
*rest)`` work similar to wildcards in 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 dict. Unlike sequence
|
||||
patterns, extra keys are ignored. A wildcard ``**rest`` is also
|
||||
supported. (But ``**_`` would be redundant, so it not allowed.)
|
||||
|
||||
- Subpatterns may be captured using the walrus (``:=``) operator::
|
||||
|
||||
case (Point(x1, y1), p2 := Point(x2, y2)): ...
|
||||
|
||||
- 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 :(")
|
||||
|
||||
- The literals ``None``, ``False`` and ``True`` are treated specially:
|
||||
comparisons to the subject are done using ``is``. This::
|
||||
|
||||
match b:
|
||||
case True:
|
||||
print("Yes!")
|
||||
|
||||
is exactly quivalent to this::
|
||||
|
||||
if b is True:
|
||||
print("Yes!")
|
||||
|
||||
- Classes may override the mapping from positional arguments to
|
||||
attributes by setting a class variable ``__match_args__``.
|
||||
Read about it in PEP 634.
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
|
|
Loading…
Reference in New Issue