2329 lines
208 KiB
HTML
2329 lines
208 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 622 – Structural Pattern Matching | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0622/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 622 – Structural Pattern Matching | peps.python.org'>
|
||
<meta property="og:description" content="This PEP proposes to add a pattern matching statement to Python, inspired by similar syntax found in Scala, Erlang, and other languages.">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0622/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="This PEP proposes to add a pattern matching statement to Python, inspired by similar syntax found in Scala, Erlang, and other languages.">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 622</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 622 – Structural Pattern Matching</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Brandt Bucher <brandt at python.org>,
|
||
Daniel F Moisset <dfmoisset at gmail.com>,
|
||
Tobias Kohn <kohnt at tobiaskohn.ch>,
|
||
Ivan Levkivskyi <levkivskyi at gmail.com>,
|
||
Guido van Rossum <guido at python.org>,
|
||
Talin <viridia at gmail.com></dd>
|
||
<dt class="field-even">BDFL-Delegate<span class="colon">:</span></dt>
|
||
<dd class="field-even"><p></p></dd>
|
||
<dt class="field-odd">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/">Python-Dev list</a></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Replaced by another succeeding PEP">Superseded</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Created<span class="colon">:</span></dt>
|
||
<dd class="field-even">23-Jun-2020</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">3.10</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">23-Jun-2020, 08-Jul-2020</dd>
|
||
<dt class="field-odd">Superseded-By<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="../pep-0634/">634</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a><ul>
|
||
<li><a class="reference internal" href="#patterns-and-shapes">Patterns and shapes</a></li>
|
||
<li><a class="reference internal" href="#syntax">Syntax</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#overview">Overview</a><ul>
|
||
<li><a class="reference internal" href="#pattern-a-new-syntactic-construct-and-destructuring">Pattern, a new syntactic construct, and destructuring</a></li>
|
||
<li><a class="reference internal" href="#matching-process">Matching process</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rationale-and-goals">Rationale and Goals</a></li>
|
||
<li><a class="reference internal" href="#syntax-and-semantics">Syntax and Semantics</a><ul>
|
||
<li><a class="reference internal" href="#patterns">Patterns</a></li>
|
||
<li><a class="reference internal" href="#the-match-statement">The <code class="docutils literal notranslate"><span class="pre">match</span></code> statement</a></li>
|
||
<li><a class="reference internal" href="#match-semantics">Match semantics</a></li>
|
||
<li><a class="reference internal" href="#allowed-patterns">Allowed patterns</a><ul>
|
||
<li><a class="reference internal" href="#literal-patterns">Literal Patterns</a></li>
|
||
<li><a class="reference internal" href="#capture-patterns">Capture Patterns</a></li>
|
||
<li><a class="reference internal" href="#wildcard-pattern">Wildcard Pattern</a></li>
|
||
<li><a class="reference internal" href="#constant-value-patterns">Constant Value Patterns</a></li>
|
||
<li><a class="reference internal" href="#sequence-patterns">Sequence Patterns</a></li>
|
||
<li><a class="reference internal" href="#mapping-patterns">Mapping Patterns</a></li>
|
||
<li><a class="reference internal" href="#class-patterns">Class Patterns</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#combining-multiple-patterns-or-patterns">Combining multiple patterns (OR patterns)</a></li>
|
||
<li><a class="reference internal" href="#guards">Guards</a></li>
|
||
<li><a class="reference internal" href="#walrus-patterns">Walrus patterns</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#runtime-specification">Runtime specification</a><ul>
|
||
<li><a class="reference internal" href="#the-match-protocol">The Match Protocol</a></li>
|
||
<li><a class="reference internal" href="#overlapping-sub-patterns">Overlapping sub-patterns</a></li>
|
||
<li><a class="reference internal" href="#special-attribute-match-args">Special attribute <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code></a></li>
|
||
<li><a class="reference internal" href="#exceptions-and-side-effects">Exceptions and side effects</a></li>
|
||
<li><a class="reference internal" href="#the-standard-library">The standard library</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#static-checkers-specification">Static checkers specification</a><ul>
|
||
<li><a class="reference internal" href="#exhaustiveness-checks">Exhaustiveness checks</a></li>
|
||
<li><a class="reference internal" href="#sealed-classes-as-algebraic-data-types">Sealed classes as algebraic data types</a></li>
|
||
<li><a class="reference internal" href="#type-erasure">Type erasure</a></li>
|
||
<li><a class="reference internal" href="#note-about-constants">Note about constants</a></li>
|
||
<li><a class="reference internal" href="#precise-type-checking-of-star-matches">Precise type checking of star matches</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#performance-considerations">Performance Considerations</a></li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
|
||
<li><a class="reference internal" href="#impacts-on-third-party-tools">Impacts on third-party tools</a></li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#example-code">Example Code</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#don-t-do-this-pattern-matching-is-hard-to-learn">Don’t do this, pattern matching is hard to learn</a></li>
|
||
<li><a class="reference internal" href="#don-t-do-this-use-existing-method-dispatching-mechanisms">Don’t do this, use existing method dispatching mechanisms</a></li>
|
||
<li><a class="reference internal" href="#allow-more-flexible-assignment-targets-instead">Allow more flexible assignment targets instead</a></li>
|
||
<li><a class="reference internal" href="#make-it-an-expression">Make it an expression</a></li>
|
||
<li><a class="reference internal" href="#use-a-hard-keyword">Use a hard keyword</a></li>
|
||
<li><a class="reference internal" href="#use-as-or-instead-of-case-for-case-clauses">Use <code class="docutils literal notranslate"><span class="pre">as</span></code> or <code class="docutils literal notranslate"><span class="pre">|</span></code> instead of <code class="docutils literal notranslate"><span class="pre">case</span></code> for case clauses</a></li>
|
||
<li><a class="reference internal" href="#use-a-flat-indentation-scheme">Use a flat indentation scheme</a></li>
|
||
<li><a class="reference internal" href="#alternatives-for-constant-value-pattern">Alternatives for constant value pattern</a></li>
|
||
<li><a class="reference internal" href="#disallow-float-literals-in-patterns">Disallow float literals in patterns</a></li>
|
||
<li><a class="reference internal" href="#range-matching-patterns">Range matching patterns</a></li>
|
||
<li><a class="reference internal" href="#use-dispatch-dict-semantics-for-matches">Use dispatch dict semantics for matches</a></li>
|
||
<li><a class="reference internal" href="#use-continue-and-break-in-case-clauses">Use <code class="docutils literal notranslate"><span class="pre">continue</span></code> and <code class="docutils literal notranslate"><span class="pre">break</span></code> in case clauses.</a></li>
|
||
<li><a class="reference internal" href="#and-patterns">AND (<code class="docutils literal notranslate"><span class="pre">&</span></code>) patterns</a></li>
|
||
<li><a class="reference internal" href="#negative-match-patterns">Negative match patterns</a></li>
|
||
<li><a class="reference internal" href="#check-exhaustiveness-at-runtime">Check exhaustiveness at runtime</a></li>
|
||
<li><a class="reference internal" href="#type-annotations-for-pattern-variables">Type annotations for pattern variables</a></li>
|
||
<li><a class="reference internal" href="#allow-rest-in-class-patterns">Allow <code class="docutils literal notranslate"><span class="pre">*rest</span></code> in class patterns</a></li>
|
||
<li><a class="reference internal" href="#disallow-a-in-constant-value-patterns">Disallow <code class="docutils literal notranslate"><span class="pre">_.a</span></code> in constant value patterns</a></li>
|
||
<li><a class="reference internal" href="#use-some-other-token-as-wildcard">Use some other token as wildcard</a></li>
|
||
<li><a class="reference internal" href="#use-some-other-syntax-instead-of-for-or-patterns">Use some other syntax instead of <code class="docutils literal notranslate"><span class="pre">|</span></code> for OR patterns</a></li>
|
||
<li><a class="reference internal" href="#add-an-else-clause">Add an <code class="docutils literal notranslate"><span class="pre">else</span></code> clause</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
|
||
<li><a class="reference internal" href="#one-off-syntax-variant">One-off syntax variant</a></li>
|
||
<li><a class="reference internal" href="#other-pattern-based-constructions">Other pattern-based constructions</a></li>
|
||
<li><a class="reference internal" href="#algebraic-matching-of-repeated-names">Algebraic matching of repeated names</a></li>
|
||
<li><a class="reference internal" href="#custom-matching-protocol">Custom matching protocol</a></li>
|
||
<li><a class="reference internal" href="#parameterized-matching-syntax">Parameterized Matching Syntax</a></li>
|
||
<li><a class="reference internal" href="#pattern-utility-library">Pattern Utility Library</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
|
||
<li><a class="reference internal" href="#version-history">Version History</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#appendix-a-full-grammar">Appendix A – Full Grammar</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP proposes to add a <strong>pattern matching statement</strong> to Python,
|
||
inspired by similar syntax found in Scala, Erlang, and other languages.</p>
|
||
<section id="patterns-and-shapes">
|
||
<h3><a class="toc-backref" href="#patterns-and-shapes" role="doc-backlink">Patterns and shapes</a></h3>
|
||
<p>The <strong>pattern syntax</strong> builds on Python’s existing syntax for sequence
|
||
unpacking (e.g., <code class="docutils literal notranslate"><span class="pre">a,</span> <span class="pre">b</span> <span class="pre">=</span> <span class="pre">value</span></code>).</p>
|
||
<p>A <code class="docutils literal notranslate"><span class="pre">match</span></code> statement compares a value (the <strong>subject</strong>)
|
||
to several different shapes (the <strong>patterns</strong>) until a shape fits.
|
||
Each pattern describes the type and structure of the accepted values
|
||
as well as the variables where to capture its contents.</p>
|
||
<p>Patterns can specify the shape to be:</p>
|
||
<ul class="simple">
|
||
<li>a sequence to be unpacked, as already mentioned</li>
|
||
<li>a mapping with specific keys</li>
|
||
<li>an instance of a given class with (optionally) specific attributes</li>
|
||
<li>a specific value</li>
|
||
<li>a wildcard</li>
|
||
</ul>
|
||
<p>Patterns can be composed in several ways.</p>
|
||
</section>
|
||
<section id="syntax">
|
||
<h3><a class="toc-backref" href="#syntax" role="doc-backlink">Syntax</a></h3>
|
||
<p>Syntactically, a <code class="docutils literal notranslate"><span class="pre">match</span></code> statement contains:</p>
|
||
<ul class="simple">
|
||
<li>a <em>subject</em> expression</li>
|
||
<li>one or more <code class="docutils literal notranslate"><span class="pre">case</span></code> clauses</li>
|
||
</ul>
|
||
<p>Each <code class="docutils literal notranslate"><span class="pre">case</span></code> clause specifies:</p>
|
||
<ul class="simple">
|
||
<li>a pattern (the overall shape to be matched)</li>
|
||
<li>an optional “guard” (a condition to be checked if the pattern matches)</li>
|
||
<li>a code block to be executed if the case clause is selected</li>
|
||
</ul>
|
||
</section>
|
||
<section id="motivation">
|
||
<h3><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h3>
|
||
<p>The rest of the PEP:</p>
|
||
<ul class="simple">
|
||
<li>motivates why we believe pattern matching makes a good addition to Python</li>
|
||
<li>explains our design choices</li>
|
||
<li>contains a precise syntactic and runtime specification</li>
|
||
<li>gives guidance for static type checkers (and one small addition to the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module)</li>
|
||
<li>discusses the main objections and alternatives that have been
|
||
brought up during extensive discussion of the proposal, both within
|
||
the group of authors and in the python-dev community</li>
|
||
</ul>
|
||
<p>Finally, we discuss some possible extensions that might be considered
|
||
in the future, once the community has ample experience with the
|
||
currently proposed syntax and semantics.</p>
|
||
</section>
|
||
</section>
|
||
<section id="overview">
|
||
<h2><a class="toc-backref" href="#overview" role="doc-backlink">Overview</a></h2>
|
||
<p>Patterns are a new syntactical category with their own rules
|
||
and special cases. Patterns mix input (given values) and output
|
||
(captured variables) in novel ways. They may take a little time to
|
||
use effectively. The authors have provided
|
||
a brief introduction to the basic concepts here. Note that this section
|
||
is not intended to be complete or entirely accurate.</p>
|
||
<section id="pattern-a-new-syntactic-construct-and-destructuring">
|
||
<h3><a class="toc-backref" href="#pattern-a-new-syntactic-construct-and-destructuring" role="doc-backlink">Pattern, a new syntactic construct, and destructuring</a></h3>
|
||
<p>A new syntactic construct called <strong>pattern</strong> is introduced in this
|
||
PEP. Syntactically, patterns look like a subset of expressions.
|
||
The following are examples of patterns:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">[first,</span> <span class="pre">second,</span> <span class="pre">*rest]</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Point2d(x,</span> <span class="pre">0)</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">{"name":</span> <span class="pre">"Bruce",</span> <span class="pre">"age":</span> <span class="pre">age}</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">42</span></code></li>
|
||
</ul>
|
||
<p>The above expressions may look like examples of object construction
|
||
with a constructor which takes some values as parameters and
|
||
builds an object from those components.</p>
|
||
<p>When viewed as a pattern, the above patterns mean the inverse operation of
|
||
construction, which we call <strong>destructuring</strong>. <strong>Destructuring</strong> takes a subject value
|
||
and extracts its components.</p>
|
||
<p>The syntactic similarity between object construction and destructuring is
|
||
intentional. It also follows the existing
|
||
Pythonic style of contexts which makes assignment targets (write contexts) look
|
||
like expressions (read contexts).</p>
|
||
<p>Pattern matching never creates objects. This is in the same way that
|
||
<code class="docutils literal notranslate"><span class="pre">[a,</span> <span class="pre">b]</span> <span class="pre">=</span> <span class="pre">my_list</span></code> doesn’t create a
|
||
new <code class="docutils literal notranslate"><span class="pre">[a,</span> <span class="pre">b]</span></code> list, nor reads the values of <code class="docutils literal notranslate"><span class="pre">a</span></code> and <code class="docutils literal notranslate"><span class="pre">b</span></code>.</p>
|
||
</section>
|
||
<section id="matching-process">
|
||
<h3><a class="toc-backref" href="#matching-process" role="doc-backlink">Matching process</a></h3>
|
||
<p>During this matching process,
|
||
the structure of the pattern may not fit the subject, and matching <em>fails</em>.</p>
|
||
<p>For example, matching the pattern <code class="docutils literal notranslate"><span class="pre">Point2d(x,</span> <span class="pre">0)</span></code> to the subject
|
||
<code class="docutils literal notranslate"><span class="pre">Point2d(3,</span> <span class="pre">0)</span></code> successfully matches. The match also <strong>binds</strong>
|
||
the pattern’s free variable <code class="docutils literal notranslate"><span class="pre">x</span></code> to the subject’s value <code class="docutils literal notranslate"><span class="pre">3</span></code>.</p>
|
||
<p>As another example, if the subject is <code class="docutils literal notranslate"><span class="pre">[3,</span> <span class="pre">0]</span></code>, the match fails
|
||
because the subject’s type <code class="docutils literal notranslate"><span class="pre">list</span></code> is not the pattern’s <code class="docutils literal notranslate"><span class="pre">Point2d</span></code>.</p>
|
||
<p>As a third example, if the subject is
|
||
<code class="docutils literal notranslate"><span class="pre">Point2d(3,</span> <span class="pre">7)</span></code>, the match fails because the
|
||
subject’s second coordinate <code class="docutils literal notranslate"><span class="pre">7</span></code> is not the same as the pattern’s <code class="docutils literal notranslate"><span class="pre">0</span></code>.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">match</span></code> statement tries to match a single subject to each of the
|
||
patterns in its <code class="docutils literal notranslate"><span class="pre">case</span></code> clauses. At the first
|
||
successful match to a pattern in a <code class="docutils literal notranslate"><span class="pre">case</span></code> clause:</p>
|
||
<ul class="simple">
|
||
<li>the variables in the pattern are assigned, and</li>
|
||
<li>a corresponding block is executed.</li>
|
||
</ul>
|
||
<p>Each <code class="docutils literal notranslate"><span class="pre">case</span></code> clause can also specify an optional boolean condition,
|
||
known as a <strong>guard</strong>.</p>
|
||
<p>Let’s look at a more detailed example of a <code class="docutils literal notranslate"><span class="pre">match</span></code> statement. The
|
||
<code class="docutils literal notranslate"><span class="pre">match</span></code> statement is used within a function to define the building
|
||
of 3D points. In this example, the function can accept as input any of
|
||
the following: tuple with 2 elements, tuple with 3 elements, an
|
||
existing Point2d object or an existing Point3d object:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">make_point_3d</span><span class="p">(</span><span class="n">pt</span><span class="p">):</span>
|
||
<span class="k">match</span> <span class="n">pt</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">Point3d</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">Point3d</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="n">Point2d</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">Point3d</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="n">Point3d</span><span class="p">(</span><span class="k">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">pt</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s2">"not a point we support"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Without pattern matching, this function’s implementation would require several
|
||
<code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> checks, one or two <code class="docutils literal notranslate"><span class="pre">len()</span></code> calls, and a more
|
||
convoluted control flow. The <code class="docutils literal notranslate"><span class="pre">match</span></code> example version and the traditional
|
||
Python version without <code class="docutils literal notranslate"><span class="pre">match</span></code> translate into similar code under the hood.
|
||
With familiarity of pattern matching, a user reading this function using <code class="docutils literal notranslate"><span class="pre">match</span></code>
|
||
will likely find this version clearer than the traditional approach.</p>
|
||
</section>
|
||
</section>
|
||
<section id="rationale-and-goals">
|
||
<h2><a class="toc-backref" href="#rationale-and-goals" role="doc-backlink">Rationale and Goals</a></h2>
|
||
<p>Python programs frequently need to handle data which varies in type,
|
||
presence of attributes/keys, or number of elements. Typical examples
|
||
are operating on nodes of a mixed structure like an AST, handling UI
|
||
events of different types, processing structured input (like
|
||
structured files or network messages), or “parsing” arguments for a
|
||
function that can accept different combinations of types and numbers
|
||
of parameters. In fact, the classic ‘visitor’ pattern is an example of this,
|
||
done in an OOP style – but matching makes it much less tedious to write.</p>
|
||
<p>Much of the code to do so tends to consist of complex chains of nested
|
||
<code class="docutils literal notranslate"><span class="pre">if</span></code>/<code class="docutils literal notranslate"><span class="pre">elif</span></code> statements, including multiple calls to <code class="docutils literal notranslate"><span class="pre">len()</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> and index/key/attribute access. Inside those branches
|
||
users sometimes need to destructure the data further to extract the
|
||
required component values, which may be nested several objects deep.</p>
|
||
<p>Pattern matching as present in many other languages provides an
|
||
elegant solution to this problem. These range from statically compiled
|
||
functional languages like F# and Haskell, via mixed-paradigm languages
|
||
like <a class="reference external" href="https://docs.scala-lang.org/tour/pattern-matching.html">Scala</a> and <a class="reference external" href="https://doc.rust-lang.org/reference/patterns.html">Rust</a>, to dynamic languages like Elixir and
|
||
Ruby, and is under consideration for JavaScript. We are indebted to
|
||
these languages for guiding the way to Pythonic pattern matching, as
|
||
Python is indebted to so many other languages for many of its
|
||
features: many basic syntactic features were inherited from C,
|
||
exceptions from Modula-3, classes were inspired by C++, slicing came
|
||
from Icon, regular expressions from Perl, decorators resemble Java
|
||
annotations, and so on.</p>
|
||
<p>The usual logic for operating on heterogeneous data can be summarized
|
||
in the following way:</p>
|
||
<ul class="simple">
|
||
<li>Some analysis is done on the <em>shape</em> (type and components) of the
|
||
data: This could involve <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> or <code class="docutils literal notranslate"><span class="pre">len()</span></code> calls and/or extracting
|
||
components (via indexing or attribute access) which are checked for
|
||
specific values or conditions.</li>
|
||
<li>If the shape is as expected, some more components are possibly
|
||
extracted and some operation is done using the extracted values.</li>
|
||
</ul>
|
||
<p>Take for example <a class="reference external" href="https://github.com/django/django/blob/5166097d7c80cab757e44f2d02f3d148fbbc2ff6/django/db/models/enums.py#L13">this piece of the Django web framework</a>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="p">(</span>
|
||
<span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">))</span> <span class="ow">and</span>
|
||
<span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">></span> <span class="mi">1</span> <span class="ow">and</span>
|
||
<span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="p">(</span><span class="n">Promise</span><span class="p">,</span> <span class="nb">str</span><span class="p">))</span>
|
||
<span class="p">):</span>
|
||
<span class="o">*</span><span class="n">value</span><span class="p">,</span> <span class="n">label</span> <span class="o">=</span> <span class="n">value</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">label</span> <span class="o">=</span> <span class="n">key</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'_'</span><span class="p">,</span> <span class="s1">' '</span><span class="p">)</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We can see the shape analysis of the <code class="docutils literal notranslate"><span class="pre">value</span></code> at the top, following
|
||
by the destructuring inside.</p>
|
||
<p>Note that shape analysis here involves checking the types both of the
|
||
container and of one of its components, and some checks on its number
|
||
of elements. Once we match the shape, we need to decompose the
|
||
sequence. With the proposal in this PEP, we could rewrite that code
|
||
into this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="o">*</span><span class="n">v</span><span class="p">,</span> <span class="n">label</span> <span class="o">:=</span> <span class="p">(</span><span class="n">Promise</span><span class="p">()</span> <span class="o">|</span> <span class="nb">str</span><span class="p">())]</span> <span class="k">if</span> <span class="n">v</span><span class="p">:</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="n">label</span> <span class="o">=</span> <span class="n">key</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'_'</span><span class="p">,</span> <span class="s1">' '</span><span class="p">)</span><span class="o">.</span><span class="n">title</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This syntax makes much more explicit which formats are possible for
|
||
the input data, and which components are extracted from where. You can
|
||
see a pattern similar to list unpacking, but also type checking: the
|
||
<code class="docutils literal notranslate"><span class="pre">Promise()</span></code> pattern is not an object construction, but represents
|
||
anything that’s an instance of <code class="docutils literal notranslate"><span class="pre">Promise</span></code>. The pattern operator <code class="docutils literal notranslate"><span class="pre">|</span></code>
|
||
separates alternative patterns (not unlike regular expressions or EBNF
|
||
grammars), and <code class="docutils literal notranslate"><span class="pre">_</span></code> is a wildcard. (Note that the match syntax used
|
||
here will accept user-defined sequences, as well as lists and tuples.)</p>
|
||
<p>In some occasions, extraction of information is not as relevant as
|
||
identifying structure. Take the following example from the
|
||
<a class="reference external" href="https://github.com/python/cpython/blob/c4cacc8/Lib/lib2to3/fixer_util.py#L158">Python standard library</a>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">is_tuple</span><span class="p">(</span><span class="n">node</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">Node</span><span class="p">)</span> <span class="ow">and</span> <span class="n">node</span><span class="o">.</span><span class="n">children</span> <span class="o">==</span> <span class="p">[</span><span class="n">LParen</span><span class="p">(),</span> <span class="n">RParen</span><span class="p">()]:</span>
|
||
<span class="k">return</span> <span class="kc">True</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">Node</span><span class="p">)</span>
|
||
<span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">children</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span>
|
||
<span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">Leaf</span><span class="p">)</span>
|
||
<span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">Node</span><span class="p">)</span>
|
||
<span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">Leaf</span><span class="p">)</span>
|
||
<span class="ow">and</span> <span class="n">node</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="s2">"("</span>
|
||
<span class="ow">and</span> <span class="n">node</span><span class="o">.</span><span class="n">children</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="s2">")"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This example shows an example of finding out the “shape” of the data
|
||
without doing significant extraction. This code is not very easy to
|
||
read, and the intended shape that this is trying to match is not
|
||
evident. Compare with the updated code using the proposed syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">is_tuple</span><span class="p">(</span><span class="n">node</span><span class="p">:</span> <span class="n">Node</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="k">match</span> <span class="n">node</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">Node</span><span class="p">(</span><span class="n">children</span><span class="o">=</span><span class="p">[</span><span class="n">LParen</span><span class="p">(),</span> <span class="n">RParen</span><span class="p">()]):</span>
|
||
<span class="k">return</span> <span class="kc">True</span>
|
||
<span class="k">case</span> <span class="n">Node</span><span class="p">(</span><span class="n">children</span><span class="o">=</span><span class="p">[</span><span class="n">Leaf</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="s2">"("</span><span class="p">),</span> <span class="n">Node</span><span class="p">(),</span> <span class="n">Leaf</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="s2">")"</span><span class="p">)]):</span>
|
||
<span class="k">return</span> <span class="kc">True</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">False</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that the proposed code will work without any modifications to the
|
||
definition of <code class="docutils literal notranslate"><span class="pre">Node</span></code> and other classes here. As shown in the
|
||
examples above, the proposal supports not just unpacking sequences, but
|
||
also doing <code class="docutils literal notranslate"><span class="pre">isinstance</span></code> checks (like <code class="docutils literal notranslate"><span class="pre">LParen()</span></code> or <code class="docutils literal notranslate"><span class="pre">str()</span></code>),
|
||
looking into object attributes (<code class="docutils literal notranslate"><span class="pre">Leaf(value="(")</span></code> for example) and
|
||
comparisons with literals.</p>
|
||
<p>That last feature helps with some kinds of code which look more like
|
||
the “switch” statement as present in other languages:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="mi">200</span><span class="p">:</span>
|
||
<span class="n">do_something</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">data</span><span class="p">)</span> <span class="c1"># OK</span>
|
||
<span class="k">case</span> <span class="mi">301</span> <span class="o">|</span> <span class="mi">302</span><span class="p">:</span>
|
||
<span class="n">retry</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">location</span><span class="p">)</span> <span class="c1"># Redirect</span>
|
||
<span class="k">case</span> <span class="mi">401</span><span class="p">:</span>
|
||
<span class="n">retry</span><span class="p">(</span><span class="n">auth</span><span class="o">=</span><span class="n">get_credentials</span><span class="p">())</span> <span class="c1"># Login first</span>
|
||
<span class="k">case</span> <span class="mi">426</span><span class="p">:</span>
|
||
<span class="n">sleep</span><span class="p">(</span><span class="n">DELAY</span><span class="p">)</span> <span class="c1"># Server is swamped, try after a bit</span>
|
||
<span class="n">retry</span><span class="p">()</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="n">RequestError</span><span class="p">(</span><span class="s2">"we couldn't get the data"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Although this will work, it’s not necessarily what the proposal is
|
||
focused on, and the new syntax has been designed to best support the
|
||
destructuring scenarios.</p>
|
||
<p>See the <a class="reference internal" href="#syntax-and-semantics">syntax</a> sections below
|
||
for a more detailed specification.</p>
|
||
<p>We propose that destructuring objects can be customized by a new
|
||
special <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> attribute. As part of this PEP we specify
|
||
the general API and its implementation for some standard library
|
||
classes (including named tuples and dataclasses). See the <a class="reference internal" href="#runtime-specification">runtime</a> section below.</p>
|
||
<p>Finally, we aim to provide comprehensive support for static type
|
||
checkers and similar tools. For this purpose, we propose to introduce
|
||
a <code class="docutils literal notranslate"><span class="pre">@typing.sealed</span></code> class decorator that will be a no-op at runtime
|
||
but will indicate to static tools that all sub-classes of this class
|
||
must be defined in the same module. This will allow effective static
|
||
exhaustiveness checks, and together with dataclasses, will provide
|
||
basic support for <a class="reference external" href="https://en.wikipedia.org/wiki/Algebraic_data_type">algebraic data types</a>. See the <a class="reference internal" href="#static-checkers-specification">static checkers</a> section for more details.</p>
|
||
</section>
|
||
<section id="syntax-and-semantics">
|
||
<h2><a class="toc-backref" href="#syntax-and-semantics" role="doc-backlink">Syntax and Semantics</a></h2>
|
||
<section id="patterns">
|
||
<h3><a class="toc-backref" href="#patterns" role="doc-backlink">Patterns</a></h3>
|
||
<p>The <strong>pattern</strong> is a new syntactic construct, that could be considered a loose
|
||
generalization of assignment targets. The key properties of a pattern are what
|
||
types and shapes of subjects it accepts, what variables it captures and how
|
||
it extracts them from the subject. For example, the pattern <code class="docutils literal notranslate"><span class="pre">[a,</span> <span class="pre">b]</span></code> matches
|
||
only sequences of exactly 2 elements, extracting the first element into <code class="docutils literal notranslate"><span class="pre">a</span></code>
|
||
and the second one into <code class="docutils literal notranslate"><span class="pre">b</span></code>.</p>
|
||
<p>This PEP defines several types of patterns. These are certainly not the
|
||
only possible ones, so the design decision was made to choose a subset of
|
||
functionality that is useful now but conservative. More patterns can be added
|
||
later as this feature gets more widespread use. See the <a class="reference internal" href="#rejected-ideas">rejected ideas</a>
|
||
and <a class="reference internal" href="#deferred-ideas">deferred ideas</a> sections for more details.</p>
|
||
<p>The patterns listed here are described in more detail below, but summarized
|
||
together in this section for simplicity:</p>
|
||
<ul class="simple">
|
||
<li>A <strong>literal pattern</strong> is useful to filter constant values in a structure.
|
||
It looks like a Python literal (including some values like <code class="docutils literal notranslate"><span class="pre">True</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">False</span></code> and <code class="docutils literal notranslate"><span class="pre">None</span></code>). It only matches objects equal to the literal, and
|
||
never binds.</li>
|
||
<li>A <strong>capture pattern</strong> looks like <code class="docutils literal notranslate"><span class="pre">x</span></code> and is equivalent to an identical
|
||
assignment target: it always matches and binds the variable
|
||
with the given (simple) name.</li>
|
||
<li>The <strong>wildcard pattern</strong> is a single underscore: <code class="docutils literal notranslate"><span class="pre">_</span></code>. It always matches,
|
||
but does not capture any variable (which prevents interference with other
|
||
uses for <code class="docutils literal notranslate"><span class="pre">_</span></code> and allows for some optimizations).</li>
|
||
<li>A <strong>constant value pattern</strong> works like the literal but for certain named
|
||
constants. Note that it must be a qualified (dotted) name, given the possible
|
||
ambiguity with a capture pattern. It looks like <code class="docutils literal notranslate"><span class="pre">Color.RED</span></code> and
|
||
only matches values equal to the corresponding value. It never binds.</li>
|
||
<li>A <strong>sequence pattern</strong> looks like <code class="docutils literal notranslate"><span class="pre">[a,</span> <span class="pre">*rest,</span> <span class="pre">b]</span></code> and is similar to
|
||
a list unpacking. An important difference is that the elements nested
|
||
within it can be any kind of patterns, not just names or sequences.
|
||
It matches only sequences of appropriate length, as long as all the sub-patterns
|
||
also match. It makes all the bindings of its sub-patterns.</li>
|
||
<li>A <strong>mapping pattern</strong> looks like <code class="docutils literal notranslate"><span class="pre">{"user":</span> <span class="pre">u,</span> <span class="pre">"emails":</span> <span class="pre">[*es]}</span></code>. It matches
|
||
mappings with at least the set of provided keys, and if all the
|
||
sub-patterns match their corresponding values. It binds whatever the
|
||
sub-patterns bind while matching with the values corresponding to the keys.
|
||
Adding <code class="docutils literal notranslate"><span class="pre">**rest</span></code> at the end of the pattern to capture extra items is allowed.</li>
|
||
<li>A <strong>class pattern</strong> is similar to the above but matches attributes instead
|
||
of keys. It looks like <code class="docutils literal notranslate"><span class="pre">datetime.date(year=y,</span> <span class="pre">day=d)</span></code>. It matches
|
||
instances of the given type, having at least the specified
|
||
attributes, as long as the attributes match with the corresponding
|
||
sub-patterns. It binds whatever the sub-patterns bind when matching with the
|
||
values of
|
||
the given attributes. An optional protocol also allows matching positional
|
||
arguments.</li>
|
||
<li>An <strong>OR pattern</strong> looks like <code class="docutils literal notranslate"><span class="pre">[*x]</span> <span class="pre">|</span> <span class="pre">{"elems":</span> <span class="pre">[*x]}</span></code>. It matches if any
|
||
of its sub-patterns match. It uses the binding for the leftmost pattern
|
||
that matched.</li>
|
||
<li>A <strong>walrus pattern</strong> looks like <code class="docutils literal notranslate"><span class="pre">d</span> <span class="pre">:=</span> <span class="pre">datetime(year=2020,</span> <span class="pre">month=m)</span></code>. It
|
||
matches only
|
||
if its sub-pattern also matches. It binds whatever the sub-pattern match does, and
|
||
also binds the named variable to the entire object.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="the-match-statement">
|
||
<h3><a class="toc-backref" href="#the-match-statement" role="doc-backlink">The <code class="docutils literal notranslate"><span class="pre">match</span></code> statement</a></h3>
|
||
<p>A simplified, approximate grammar for the proposed syntax is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">...</span>
|
||
<span class="n">compound_statement</span><span class="p">:</span>
|
||
<span class="o">|</span> <span class="n">if_stmt</span>
|
||
<span class="o">...</span>
|
||
<span class="o">|</span> <span class="n">match_stmt</span>
|
||
<span class="n">match_stmt</span><span class="p">:</span> <span class="s2">"match"</span> <span class="n">expression</span> <span class="s1">':'</span> <span class="n">NEWLINE</span> <span class="n">INDENT</span> <span class="n">case_block</span><span class="o">+</span> <span class="n">DEDENT</span>
|
||
<span class="n">case_block</span><span class="p">:</span> <span class="s2">"case"</span> <span class="n">pattern</span> <span class="p">[</span><span class="n">guard</span><span class="p">]</span> <span class="s1">':'</span> <span class="n">block</span>
|
||
<span class="n">guard</span><span class="p">:</span> <span class="s1">'if'</span> <span class="n">expression</span>
|
||
<span class="n">pattern</span><span class="p">:</span> <span class="n">walrus_pattern</span> <span class="o">|</span> <span class="n">or_pattern</span>
|
||
<span class="n">walrus_pattern</span><span class="p">:</span> <span class="n">NAME</span> <span class="s1">':='</span> <span class="n">or_pattern</span>
|
||
<span class="n">or_pattern</span><span class="p">:</span> <span class="n">closed_pattern</span> <span class="p">(</span><span class="s1">'|'</span> <span class="n">closed_pattern</span><span class="p">)</span><span class="o">*</span>
|
||
<span class="n">closed_pattern</span><span class="p">:</span>
|
||
<span class="o">|</span> <span class="n">literal_pattern</span>
|
||
<span class="o">|</span> <span class="n">capture_pattern</span>
|
||
<span class="o">|</span> <span class="n">wildcard_pattern</span>
|
||
<span class="o">|</span> <span class="n">constant_pattern</span>
|
||
<span class="o">|</span> <span class="n">sequence_pattern</span>
|
||
<span class="o">|</span> <span class="n">mapping_pattern</span>
|
||
<span class="o">|</span> <span class="n">class_pattern</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>See <a class="reference internal" href="#appendix-a-full-grammar">Appendix A</a> for the full, unabridged grammar.
|
||
The simplified grammars in this section are there for helping the reader,
|
||
not as a full specification.</p>
|
||
<p>We propose that the match operation should be a statement, not an expression.
|
||
Although in
|
||
many languages it is an expression, being a statement better suits the general
|
||
logic of Python syntax. See <a class="reference internal" href="#rejected-ideas">rejected ideas</a> for more discussion.
|
||
The allowed patterns are described in detail below in the <a class="reference internal" href="#allowed-patterns">patterns</a> subsection.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">match</span></code> and <code class="docutils literal notranslate"><span class="pre">case</span></code> keywords are proposed to be soft keywords,
|
||
so that they are recognized as keywords at the beginning of a match
|
||
statement or case block respectively, but are allowed to be used in
|
||
other places as variable or argument names.</p>
|
||
<p>The proposed indentation structure is as following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">some_expression</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">pattern_1</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">pattern_2</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here, <code class="docutils literal notranslate"><span class="pre">some_expression</span></code> represents the value that is being matched against,
|
||
which will be referred to hereafter as the <em>subject</em> of the match.</p>
|
||
</section>
|
||
<section id="match-semantics">
|
||
<h3><a class="toc-backref" href="#match-semantics" role="doc-backlink">Match semantics</a></h3>
|
||
<p>The proposed large scale semantics for choosing the match is to choose the first
|
||
matching pattern and execute the corresponding suite. The remaining patterns
|
||
are not tried. If there are no matching patterns, the statement ‘falls
|
||
through’, and execution continues at the following statement.</p>
|
||
<p>Essentially this is equivalent to a chain of <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">...</span> <span class="pre">elif</span> <span class="pre">...</span> <span class="pre">else</span></code>
|
||
statements. Note that unlike for the previously proposed <code class="docutils literal notranslate"><span class="pre">switch</span></code> statement,
|
||
the pre-computed dispatch dictionary semantics does not apply here.</p>
|
||
<p>There is no <code class="docutils literal notranslate"><span class="pre">default</span></code> or <code class="docutils literal notranslate"><span class="pre">else</span></code> case - instead the special wildcard
|
||
<code class="docutils literal notranslate"><span class="pre">_</span></code> can be used (see the section on <a class="reference internal" href="#capture-patterns">capture_pattern</a>)
|
||
as a final ‘catch-all’ pattern.</p>
|
||
<p>Name bindings made during a successful pattern match outlive the executed suite
|
||
and can be used after the match statement. This follows the logic of other
|
||
Python statements that can bind names, such as <code class="docutils literal notranslate"><span class="pre">for</span></code> loop and <code class="docutils literal notranslate"><span class="pre">with</span></code>
|
||
statement. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">shape</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="n">Rectangle</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="k">_</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="c1"># This works</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>During failed pattern matches, some sub-patterns may succeed. For example,
|
||
while matching the value <code class="docutils literal notranslate"><span class="pre">[0,</span> <span class="pre">1,</span> <span class="pre">2]</span></code> with the pattern <code class="docutils literal notranslate"><span class="pre">(0,</span> <span class="pre">x,</span> <span class="pre">1)</span></code>, the
|
||
sub-pattern <code class="docutils literal notranslate"><span class="pre">x</span></code> may succeed if the list elements are matched from left to right.
|
||
The implementation may choose to either make persistent bindings for those
|
||
partial matches or not. User code including a <code class="docutils literal notranslate"><span class="pre">match</span></code> statement should not rely
|
||
on the bindings being made for a failed match, but also shouldn’t assume that
|
||
variables are unchanged by a failed match. This part of the behavior is
|
||
left intentionally unspecified so different implementations can add
|
||
optimizations, and to prevent introducing semantic restrictions that could
|
||
limit the extensibility of this feature.</p>
|
||
<p>Note that some pattern types below define more specific rules about when
|
||
the binding is made.</p>
|
||
</section>
|
||
<section id="allowed-patterns">
|
||
<span id="id1"></span><h3><a class="toc-backref" href="#allowed-patterns" role="doc-backlink">Allowed patterns</a></h3>
|
||
<p>We introduce the proposed syntax gradually. Here we start from the main
|
||
building blocks. The following patterns are supported:</p>
|
||
<section id="literal-patterns">
|
||
<h4><a class="toc-backref" href="#literal-patterns" role="doc-backlink">Literal Patterns</a></h4>
|
||
<p>Simplified syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">literal_pattern</span><span class="p">:</span>
|
||
<span class="o">|</span> <span class="n">number</span>
|
||
<span class="o">|</span> <span class="n">string</span>
|
||
<span class="o">|</span> <span class="s1">'None'</span>
|
||
<span class="o">|</span> <span class="s1">'True'</span>
|
||
<span class="o">|</span> <span class="s1">'False'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A literal pattern consists of a simple literal like a string, a number,
|
||
a Boolean literal (<code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code>), or <code class="docutils literal notranslate"><span class="pre">None</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">number</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Nothing"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Just one"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="mi">2</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"A couple"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"One less than nothing"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="mi">1</span><span class="o">-</span><span class="mi">1</span><span class="n">j</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Good luck with that..."</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Literal pattern uses equality with literal on the right hand side, so that
|
||
in the above example <code class="docutils literal notranslate"><span class="pre">number</span> <span class="pre">==</span> <span class="pre">0</span></code> and then possibly <code class="docutils literal notranslate"><span class="pre">number</span> <span class="pre">==</span> <span class="pre">1</span></code>, etc
|
||
will be evaluated. Note that although technically negative numbers
|
||
are represented using unary minus, they are considered
|
||
literals for the purpose of pattern matching. Unary plus is not allowed.
|
||
Binary plus and minus are allowed only to join a real number and an imaginary
|
||
number to form a complex number, such as <code class="docutils literal notranslate"><span class="pre">1+1j</span></code>.</p>
|
||
<p>Note that because equality (<code class="docutils literal notranslate"><span class="pre">__eq__</span></code>) is used, and the equivalency
|
||
between Booleans and the integers <code class="docutils literal notranslate"><span class="pre">0</span></code> and <code class="docutils literal notranslate"><span class="pre">1</span></code>, there is no
|
||
practical difference between the following two:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="kc">True</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Triple-quoted strings are supported. Raw strings and byte strings
|
||
are supported. F-strings are not allowed (since in general they are not
|
||
really literals).</p>
|
||
</section>
|
||
<section id="capture-patterns">
|
||
<h4><a class="toc-backref" href="#capture-patterns" role="doc-backlink">Capture Patterns</a></h4>
|
||
<p>Simplified syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">capture_pattern</span><span class="p">:</span> <span class="n">NAME</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A capture pattern serves as an assignment target for the matched expression:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">greeting</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="s2">""</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Hello!"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="n">name</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Hi </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Only a single name is allowed (a dotted name is a constant value pattern).
|
||
A capture pattern always succeeds. A capture pattern appearing in a scope makes
|
||
the name local to that scope. For example, using <code class="docutils literal notranslate"><span class="pre">name</span></code> after the above
|
||
snippet may raise <code class="docutils literal notranslate"><span class="pre">UnboundLocalError</span></code> rather than <code class="docutils literal notranslate"><span class="pre">NameError</span></code>, if
|
||
the <code class="docutils literal notranslate"><span class="pre">""</span></code> case clause was taken:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">greeting</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="s2">""</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Hello!"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="n">name</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Hi </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">"Santa"</span><span class="p">:</span> <span class="c1"># <-- might raise UnboundLocalError</span>
|
||
<span class="o">...</span> <span class="c1"># but works fine if greeting was not empty</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>While matching against each case clause, a name may be bound at most
|
||
once, having two capture patterns with coinciding names is an error:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">data</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="p">]:</span> <span class="c1"># Error!</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note: one can still match on a collection with equal items using <a class="reference internal" href="#guards">guards</a>.
|
||
Also, <code class="docutils literal notranslate"><span class="pre">[x,</span> <span class="pre">y]</span> <span class="pre">|</span> <span class="pre">Point(x,</span> <span class="pre">y)</span></code> is a legal pattern because the two
|
||
alternatives are never matched at the same time.</p>
|
||
<p>The single underscore (<code class="docutils literal notranslate"><span class="pre">_</span></code>) is not considered a <code class="docutils literal notranslate"><span class="pre">NAME</span></code> and treated specially
|
||
as a <a class="reference internal" href="#wildcard-pattern">wildcard pattern</a>.</p>
|
||
<p>Reminder: <code class="docutils literal notranslate"><span class="pre">None</span></code>, <code class="docutils literal notranslate"><span class="pre">False</span></code> and <code class="docutils literal notranslate"><span class="pre">True</span></code> are keywords denoting
|
||
literals, not names.</p>
|
||
</section>
|
||
<section id="wildcard-pattern">
|
||
<h4><a class="toc-backref" href="#wildcard-pattern" role="doc-backlink">Wildcard Pattern</a></h4>
|
||
<p>Simplified syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">wildcard_pattern</span><span class="p">:</span> <span class="s2">"_"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The single underscore (<code class="docutils literal notranslate"><span class="pre">_</span></code>) name is a special kind of pattern that always
|
||
matches but <em>never</em> binds:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">data</span><span class="p">:</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="p">[</span><span class="k">_</span><span class="p">,</span> <span class="n">_</span><span class="p">]:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Some pair"</span><span class="p">)</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="c1"># Error!</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Given that no binding is made, it can be used as many times as desired, unlike
|
||
capture patterns.</p>
|
||
</section>
|
||
<section id="constant-value-patterns">
|
||
<h4><a class="toc-backref" href="#constant-value-patterns" role="doc-backlink">Constant Value Patterns</a></h4>
|
||
<p>Simplified syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">constant_pattern</span><span class="p">:</span> <span class="n">NAME</span> <span class="p">(</span><span class="s1">'.'</span> <span class="n">NAME</span><span class="p">)</span><span class="o">+</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This is used to match against constants and enum values.
|
||
Every dotted name in a pattern is looked up using normal Python name
|
||
resolution rules, and the value is used for comparison by equality with
|
||
the match subject (same as for literals):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Sides</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">Enum</span><span class="p">):</span>
|
||
<span class="n">SPAM</span> <span class="o">=</span> <span class="s2">"Spam"</span>
|
||
<span class="n">EGGS</span> <span class="o">=</span> <span class="s2">"eggs"</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">match</span> <span class="n">entree</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
|
||
<span class="k">case</span> <span class="n">Sides</span><span class="o">.</span><span class="n">SPAM</span><span class="p">:</span> <span class="c1"># Compares entree[-1] == Sides.SPAM.</span>
|
||
<span class="n">response</span> <span class="o">=</span> <span class="s2">"Have you got anything without Spam?"</span>
|
||
<span class="k">case</span> <span class="n">side</span><span class="p">:</span> <span class="c1"># Assigns side = entree[-1].</span>
|
||
<span class="n">response</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"Well, could I have their Spam instead of the </span><span class="si">{</span><span class="n">side</span><span class="si">}</span><span class="s2"> then?"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that there is no way to use unqualified names as constant value
|
||
patterns (they always denote variables to be captured). See
|
||
<a class="reference internal" href="#rejected-ideas">rejected ideas</a> for other syntactic alternatives that were
|
||
considered for constant value patterns.</p>
|
||
</section>
|
||
<section id="sequence-patterns">
|
||
<h4><a class="toc-backref" href="#sequence-patterns" role="doc-backlink">Sequence Patterns</a></h4>
|
||
<p>Simplified syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>sequence_pattern:
|
||
| '[' [values_pattern] ']'
|
||
| '(' [value_pattern ',' [values pattern]] ')'
|
||
values_pattern: ','.value_pattern+ ','?
|
||
value_pattern: '*' capture_pattern | pattern
|
||
</pre></div>
|
||
</div>
|
||
<p>A sequence pattern follows the same semantics as unpacking assignment.
|
||
Like unpacking assignment, both tuple-like and list-like syntax can be
|
||
used, with identical semantics. Each element can be an arbitrary
|
||
pattern; there may also be at most one <code class="docutils literal notranslate"><span class="pre">*name</span></code> pattern to catch all
|
||
remaining items:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">collection</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="mi">1</span><span class="p">,</span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="n">others</span><span class="p">]:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Got 1 and a nested sequence"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Got 1 and </span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To match a sequence pattern the subject must be an instance of
|
||
<code class="docutils literal notranslate"><span class="pre">collections.abc.Sequence</span></code>, and it cannot be any kind of string
|
||
(<code class="docutils literal notranslate"><span class="pre">str</span></code>, <code class="docutils literal notranslate"><span class="pre">bytes</span></code>, <code class="docutils literal notranslate"><span class="pre">bytearray</span></code>). It cannot be an iterator. For matching
|
||
on a specific collection class, see class pattern below.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">_</span></code> wildcard can be starred to match sequences of varying lengths. For
|
||
example:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">[*_]</span></code> matches a sequence of any length.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">(_,</span> <span class="pre">_,</span> <span class="pre">*_)</span></code>, matches any sequence of length two or more.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">["a",</span> <span class="pre">*_,</span> <span class="pre">"z"]</span></code> matches any sequence of length two or more that starts with
|
||
<code class="docutils literal notranslate"><span class="pre">"a"</span></code> and ends with <code class="docutils literal notranslate"><span class="pre">"z"</span></code>.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="mapping-patterns">
|
||
<h4><a class="toc-backref" href="#mapping-patterns" role="doc-backlink">Mapping Patterns</a></h4>
|
||
<p>Simplified syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>mapping_pattern: '{' [items_pattern] '}'
|
||
items_pattern: ','.key_value_pattern+ ','?
|
||
key_value_pattern:
|
||
| (literal_pattern | constant_pattern) ':' or_pattern
|
||
| '**' capture_pattern
|
||
</pre></div>
|
||
</div>
|
||
<p>Mapping pattern is a generalization of iterable unpacking to mappings.
|
||
Its syntax is similar to dictionary display but each key and value are
|
||
patterns <code class="docutils literal notranslate"><span class="pre">"{"</span> <span class="pre">(pattern</span> <span class="pre">":"</span> <span class="pre">pattern)+</span> <span class="pre">"}"</span></code>. A <code class="docutils literal notranslate"><span class="pre">**rest</span></code> pattern is also
|
||
allowed, to extract the remaining items. Only literal and constant value
|
||
patterns are allowed in key positions:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">constants</span>
|
||
|
||
<span class="k">match</span> <span class="n">config</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">{</span><span class="s2">"route"</span><span class="p">:</span> <span class="n">route</span><span class="p">}:</span>
|
||
<span class="n">process_route</span><span class="p">(</span><span class="n">route</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="p">{</span><span class="n">constants</span><span class="o">.</span><span class="n">DEFAULT_PORT</span><span class="p">:</span> <span class="n">sub_config</span><span class="p">,</span> <span class="o">**</span><span class="n">rest</span><span class="p">}:</span>
|
||
<span class="n">process_config</span><span class="p">(</span><span class="n">sub_config</span><span class="p">,</span> <span class="n">rest</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The subject must be an instance of <code class="docutils literal notranslate"><span class="pre">collections.abc.Mapping</span></code>.
|
||
Extra keys in the subject are ignored even if <code class="docutils literal notranslate"><span class="pre">**rest</span></code> is not present.
|
||
This is different from sequence pattern, where extra items will cause a
|
||
match to fail. But mappings are actually different from sequences: they
|
||
have natural structural sub-typing behavior, i.e., passing a dictionary
|
||
with extra keys somewhere will likely just work.</p>
|
||
<p>For this reason, <code class="docutils literal notranslate"><span class="pre">**_</span></code> is invalid in mapping patterns; it would always be a
|
||
no-op that could be removed without consequence.</p>
|
||
<p>Matched key-value pairs must already be present in the mapping, and not created
|
||
on-the-fly by <code class="docutils literal notranslate"><span class="pre">__missing__</span></code> or <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code>. For example,
|
||
<code class="docutils literal notranslate"><span class="pre">collections.defaultdict</span></code> instances will only match patterns with keys that
|
||
were already present when the <code class="docutils literal notranslate"><span class="pre">match</span></code> block was entered.</p>
|
||
</section>
|
||
<section id="class-patterns">
|
||
<h4><a class="toc-backref" href="#class-patterns" role="doc-backlink">Class Patterns</a></h4>
|
||
<p>Simplified syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>class_pattern:
|
||
| name_or_attr '(' ')'
|
||
| name_or_attr '(' ','.pattern+ ','? ')'
|
||
| name_or_attr '(' ','.keyword_pattern+ ','? ')'
|
||
| name_or_attr '(' ','.pattern+ ',' ','.keyword_pattern+ ','? ')'
|
||
keyword_pattern: NAME '=' or_pattern
|
||
</pre></div>
|
||
</div>
|
||
<p>A class pattern provides support for destructuring arbitrary objects.
|
||
There are two possible ways of matching on object attributes: by position
|
||
like <code class="docutils literal notranslate"><span class="pre">Point(1,</span> <span class="pre">2)</span></code>, and by name like <code class="docutils literal notranslate"><span class="pre">Point(x=1,</span> <span class="pre">y=2)</span></code>. These
|
||
two can be combined, but a positional match cannot follow a match by name.
|
||
Each item in a class pattern can be an arbitrary pattern. A simple
|
||
example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">shape</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">x0</span><span class="p">,</span> <span class="n">y0</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span><span class="p">,</span> <span class="n">painted</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Whether a match succeeds or not is determined by the equivalent of an
|
||
<code class="docutils literal notranslate"><span class="pre">isinstance</span></code> call. If the subject (<code class="docutils literal notranslate"><span class="pre">shape</span></code>, in the example) is not
|
||
an instance of the named class (<code class="docutils literal notranslate"><span class="pre">Point</span></code> or <code class="docutils literal notranslate"><span class="pre">Rectangle</span></code>), the match
|
||
fails. Otherwise, it continues (see details in the <a class="reference internal" href="#runtime-specification">runtime</a> section).</p>
|
||
<p>The named class must inherit from <code class="docutils literal notranslate"><span class="pre">type</span></code>. It may be a single name
|
||
or a dotted name (e.g. <code class="docutils literal notranslate"><span class="pre">some_mod.SomeClass</span></code> or <code class="docutils literal notranslate"><span class="pre">mod.pkg.Class</span></code>).
|
||
The leading name must not be <code class="docutils literal notranslate"><span class="pre">_</span></code>, so e.g. <code class="docutils literal notranslate"><span class="pre">_(...)</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">_.C(...)</span></code> are invalid. Use <code class="docutils literal notranslate"><span class="pre">object(foo=_)</span></code> to check whether the
|
||
matched object has an attribute <code class="docutils literal notranslate"><span class="pre">foo</span></code>.</p>
|
||
<p>By default, sub-patterns may only be matched by keyword for
|
||
user-defined classes. In order to support positional sub-patterns, a
|
||
custom <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> attribute is required.
|
||
The runtime allows matching against
|
||
arbitrarily nested patterns by chaining all of the instance checks and
|
||
attribute lookups appropriately.</p>
|
||
</section>
|
||
</section>
|
||
<section id="combining-multiple-patterns-or-patterns">
|
||
<h3><a class="toc-backref" href="#combining-multiple-patterns-or-patterns" role="doc-backlink">Combining multiple patterns (OR patterns)</a></h3>
|
||
<p>Multiple alternative patterns can be combined into one using <code class="docutils literal notranslate"><span class="pre">|</span></code>. This means
|
||
the whole pattern matches if at least one alternative matches.
|
||
Alternatives are tried from left to right and have a short-circuit property,
|
||
subsequent patterns are not tried if one matched. Examples:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">something</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="mi">0</span> <span class="o">|</span> <span class="mi">1</span> <span class="o">|</span> <span class="mi">2</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Small number"</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="p">[]</span> <span class="o">|</span> <span class="p">[</span><span class="k">_</span><span class="p">]:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"A short sequence"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="nb">str</span><span class="p">()</span> <span class="o">|</span> <span class="nb">bytes</span><span class="p">():</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Something string-like"</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Something else"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The alternatives may bind variables, as long as each alternative binds
|
||
the same set of variables (excluding <code class="docutils literal notranslate"><span class="pre">_</span></code>). For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">something</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="mi">1</span> <span class="o">|</span> <span class="n">x</span><span class="p">:</span> <span class="c1"># Error!</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">x</span> <span class="o">|</span> <span class="mi">1</span><span class="p">:</span> <span class="c1"># Error!</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">one</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">|</span> <span class="n">two</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="c1"># Error!</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">Foo</span><span class="p">(</span><span class="n">arg</span><span class="o">=</span><span class="n">x</span><span class="p">)</span> <span class="o">|</span> <span class="n">Bar</span><span class="p">(</span><span class="n">arg</span><span class="o">=</span><span class="n">x</span><span class="p">):</span> <span class="c1"># Valid, both arms bind 'x'</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">|</span> <span class="n">x</span><span class="p">:</span> <span class="c1"># Valid, both arms bind 'x'</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="guards">
|
||
<h3><a class="toc-backref" href="#guards" role="doc-backlink">Guards</a></h3>
|
||
<p>Each <em>top-level</em> pattern can be followed by a <strong>guard</strong> of the form
|
||
<code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">expression</span></code>. A case clause succeeds if the pattern matches and the guard
|
||
evaluates to a true value. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="nb">input</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]</span> <span class="k">if</span> <span class="n">x</span> <span class="o">></span> <span class="n">MAX_INT</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">></span> <span class="n">MAX_INT</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Got a pair of large numbers"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="n">x</span> <span class="k">if</span> <span class="n">x</span> <span class="o">></span> <span class="n">MAX_INT</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Got a large number"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]</span> <span class="k">if</span> <span class="n">x</span> <span class="o">==</span> <span class="n">y</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Got equal items"</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Not an outstanding input"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If evaluating a guard raises an exception, it is propagated onwards rather
|
||
than fail the case clause. Names that appear in a pattern are bound before the
|
||
guard succeeds. So this will work:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">values</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||
|
||
<span class="k">match</span> <span class="n">values</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="k">if</span> <span class="n">x</span><span class="p">:</span>
|
||
<span class="o">...</span> <span class="c1"># This is not executed</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1"># This will print "0"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that guards are not allowed for nested patterns, so that <code class="docutils literal notranslate"><span class="pre">[x</span> <span class="pre">if</span> <span class="pre">x</span> <span class="pre">></span> <span class="pre">0]</span></code>
|
||
is a <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> and <code class="docutils literal notranslate"><span class="pre">1</span> <span class="pre">|</span> <span class="pre">2</span> <span class="pre">if</span> <span class="pre">3</span> <span class="pre">|</span> <span class="pre">4</span></code> will be parsed as
|
||
<code class="docutils literal notranslate"><span class="pre">(1</span> <span class="pre">|</span> <span class="pre">2)</span> <span class="pre">if</span> <span class="pre">(3</span> <span class="pre">|</span> <span class="pre">4)</span></code>.</p>
|
||
</section>
|
||
<section id="walrus-patterns">
|
||
<h3><a class="toc-backref" href="#walrus-patterns" role="doc-backlink">Walrus patterns</a></h3>
|
||
<p>It is often useful to match a sub-pattern <em>and</em> bind the corresponding
|
||
value to a name. For example, it can be useful to write more efficient
|
||
matches, or simply to avoid repetition. To simplify such cases, any pattern
|
||
(other than the walrus pattern itself) can be preceded by a name and
|
||
the walrus operator (<code class="docutils literal notranslate"><span class="pre">:=</span></code>). For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">get_shape</span><span class="p">():</span>
|
||
<span class="k">case</span> <span class="n">Line</span><span class="p">(</span><span class="n">start</span> <span class="o">:=</span> <span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="n">end</span><span class="p">)</span> <span class="k">if</span> <span class="n">start</span> <span class="o">==</span> <span class="n">end</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Zero length line at </span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="n">y</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The name on the left of the walrus operator can be used in a guard, in
|
||
the match suite, or after the match statement. However, the name will
|
||
<em>only</em> be bound if the sub-pattern succeeds. Another example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">group_shapes</span><span class="p">():</span>
|
||
<span class="k">case</span> <span class="p">[],</span> <span class="p">[</span><span class="n">point</span> <span class="o">:=</span> <span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="o">*</span><span class="n">other</span><span class="p">]:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Got </span><span class="si">{</span><span class="n">point</span><span class="si">}</span><span class="s2"> in the second group"</span><span class="p">)</span>
|
||
<span class="n">process_coordinates</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Technically, most such examples can be rewritten using guards and/or nested
|
||
match statements, but this will be less readable and/or will produce less
|
||
efficient code. Essentially, most of the arguments in <a class="pep reference internal" href="../pep-0572/" title="PEP 572 – Assignment Expressions">PEP 572</a> apply here
|
||
equally.</p>
|
||
<p>The wildcard <code class="docutils literal notranslate"><span class="pre">_</span></code> is not a valid name here.</p>
|
||
</section>
|
||
</section>
|
||
<section id="runtime-specification">
|
||
<h2><a class="toc-backref" href="#runtime-specification" role="doc-backlink">Runtime specification</a></h2>
|
||
<section id="the-match-protocol">
|
||
<h3><a class="toc-backref" href="#the-match-protocol" role="doc-backlink">The Match Protocol</a></h3>
|
||
<p>The equivalent of an <code class="docutils literal notranslate"><span class="pre">isinstance</span></code> call is used to decide whether an
|
||
object matches a given class pattern and to extract the corresponding
|
||
attributes. Classes requiring different matching semantics (such as
|
||
duck-typing) can do so by defining <code class="docutils literal notranslate"><span class="pre">__instancecheck__</span></code> (a
|
||
pre-existing metaclass hook) or by using <code class="docutils literal notranslate"><span class="pre">typing.Protocol</span></code>.</p>
|
||
<p>The procedure is as following:</p>
|
||
<ul class="simple">
|
||
<li>The class object for <code class="docutils literal notranslate"><span class="pre">Class</span></code> in <code class="docutils literal notranslate"><span class="pre">Class(<sub-patterns>)</span></code> is
|
||
looked up and <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">Class)</span></code> is called, where <code class="docutils literal notranslate"><span class="pre">obj</span></code> is
|
||
the value being matched. If false, the match fails.</li>
|
||
<li>Otherwise, if any sub-patterns are given in the form of positional
|
||
or keyword arguments, these are matched from left to right, as
|
||
follows. The match fails as soon as a sub-pattern fails; if all
|
||
sub-patterns succeed, the overall class pattern match succeeds.</li>
|
||
<li>If there are match-by-position items and the class has a
|
||
<code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> attribute, the item at position <code class="docutils literal notranslate"><span class="pre">i</span></code>
|
||
is matched against the value looked up by attribute
|
||
<code class="docutils literal notranslate"><span class="pre">__match_args__[i]</span></code>. For example, a pattern <code class="docutils literal notranslate"><span class="pre">Point2d(5,</span> <span class="pre">8)</span></code>,
|
||
where <code class="docutils literal notranslate"><span class="pre">Point2d.__match_args__</span> <span class="pre">==</span> <span class="pre">["x",</span> <span class="pre">"y"]</span></code>, is translated
|
||
(approximately) into <code class="docutils literal notranslate"><span class="pre">obj.x</span> <span class="pre">==</span> <span class="pre">5</span> <span class="pre">and</span> <span class="pre">obj.y</span> <span class="pre">==</span> <span class="pre">8</span></code>.</li>
|
||
<li>If there are more positional items than the length of
|
||
<code class="docutils literal notranslate"><span class="pre">__match_args__</span></code>, a <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> is raised.</li>
|
||
<li>If the <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> attribute is absent on the matched class,
|
||
and one or more positional item appears in a match,
|
||
<code class="docutils literal notranslate"><span class="pre">TypeError</span></code> is also raised. We don’t fall back on
|
||
using <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> or <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> – “In the face of ambiguity,
|
||
refuse the temptation to guess.”</li>
|
||
<li>If there are any match-by-keyword items the keywords are looked up
|
||
as attributes on the subject. If the lookup succeeds the value is
|
||
matched against the corresponding sub-pattern. If the lookup fails,
|
||
the match fails.</li>
|
||
</ul>
|
||
<p>Such a protocol favors simplicity of implementation over flexibility and
|
||
performance. For other considered alternatives, see <a class="reference internal" href="#extended-matching">extended matching</a>.</p>
|
||
<p>For the most commonly-matched built-in types (<code class="docutils literal notranslate"><span class="pre">bool</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">bytearray</span></code>, <code class="docutils literal notranslate"><span class="pre">bytes</span></code>, <code class="docutils literal notranslate"><span class="pre">dict</span></code>, <code class="docutils literal notranslate"><span class="pre">float</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">frozenset</span></code>, <code class="docutils literal notranslate"><span class="pre">int</span></code>, <code class="docutils literal notranslate"><span class="pre">list</span></code>, <code class="docutils literal notranslate"><span class="pre">set</span></code>, <code class="docutils literal notranslate"><span class="pre">str</span></code>, and <code class="docutils literal notranslate"><span class="pre">tuple</span></code>), a
|
||
single positional sub-pattern is allowed to be passed to
|
||
the call. Rather than being matched against any particular attribute
|
||
on the subject, it is instead matched against the subject itself. This
|
||
creates behavior that is useful and intuitive for these objects:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">bool(False)</span></code> matches <code class="docutils literal notranslate"><span class="pre">False</span></code> (but not <code class="docutils literal notranslate"><span class="pre">0</span></code>).</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">tuple((0,</span> <span class="pre">1,</span> <span class="pre">2))</span></code> matches <code class="docutils literal notranslate"><span class="pre">(0,</span> <span class="pre">1,</span> <span class="pre">2)</span></code> (but not <code class="docutils literal notranslate"><span class="pre">[0,</span> <span class="pre">1,</span> <span class="pre">2]</span></code>).</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">int(i)</span></code> matches any <code class="docutils literal notranslate"><span class="pre">int</span></code> and binds it to the name <code class="docutils literal notranslate"><span class="pre">i</span></code>.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="overlapping-sub-patterns">
|
||
<h3><a class="toc-backref" href="#overlapping-sub-patterns" role="doc-backlink">Overlapping sub-patterns</a></h3>
|
||
<p>Certain classes of overlapping matches are detected at
|
||
runtime and will raise exceptions. In addition to basic checks
|
||
described in the previous subsection:</p>
|
||
<ul class="simple">
|
||
<li>The interpreter will check that two match items are not targeting the same
|
||
attribute, for example <code class="docutils literal notranslate"><span class="pre">Point2d(1,</span> <span class="pre">2,</span> <span class="pre">y=3)</span></code> is an error.</li>
|
||
<li>It will also check that a mapping pattern does not attempt to match
|
||
the same key more than once.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="special-attribute-match-args">
|
||
<h3><a class="toc-backref" href="#special-attribute-match-args" role="doc-backlink">Special attribute <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code></a></h3>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> attribute is always looked up on the type
|
||
object named in the pattern. If present, it must be a list or tuple
|
||
of strings naming the allowed positional arguments.</p>
|
||
<p>In deciding what names should be available for matching, the
|
||
recommended practice is that class patterns should be the mirror of
|
||
construction; that is, the set of available names and their types
|
||
should resemble the arguments to <code class="docutils literal notranslate"><span class="pre">__init__()</span></code>.</p>
|
||
<p>Only match-by-name will work by default, and classes should define
|
||
<code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> as a class attribute if they would like to support
|
||
match-by-position. Additionally, dataclasses and named tuples will
|
||
support match-by-position out of the box. See below for more details.</p>
|
||
</section>
|
||
<section id="exceptions-and-side-effects">
|
||
<h3><a class="toc-backref" href="#exceptions-and-side-effects" role="doc-backlink">Exceptions and side effects</a></h3>
|
||
<p>While matching each case, the <code class="docutils literal notranslate"><span class="pre">match</span></code> statement may trigger execution of other
|
||
functions (for example <code class="docutils literal notranslate"><span class="pre">__getitem__()</span></code>, <code class="docutils literal notranslate"><span class="pre">__len__()</span></code> or
|
||
a property). Almost every exception caused by those propagates outside of the
|
||
match statement normally. The only case where an exception is not propagated is
|
||
an <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> raised while trying to lookup an attribute while matching
|
||
attributes of a Class Pattern; that case results in just a matching failure,
|
||
and the rest of the statement proceeds normally.</p>
|
||
<p>The only side-effect carried on explicitly by the matching process is the binding of
|
||
names. However, the process relies on attribute access,
|
||
instance checks, <code class="docutils literal notranslate"><span class="pre">len()</span></code>, equality and item access on the subject and some of
|
||
its components. It also evaluates constant value patterns and the left side of
|
||
class patterns. While none of those typically create any side-effects, some of
|
||
these objects could. This proposal intentionally leaves out any specification
|
||
of what methods are called or how many times. User code relying on that
|
||
behavior should be considered buggy.</p>
|
||
</section>
|
||
<section id="the-standard-library">
|
||
<h3><a class="toc-backref" href="#the-standard-library" role="doc-backlink">The standard library</a></h3>
|
||
<p>To facilitate the use of pattern matching, several changes will be made to
|
||
the standard library:</p>
|
||
<ul class="simple">
|
||
<li>Namedtuples and dataclasses will have auto-generated <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code>.</li>
|
||
<li>For dataclasses the order of attributes in the generated <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code>
|
||
will be the same as the order of corresponding arguments in the generated
|
||
<code class="docutils literal notranslate"><span class="pre">__init__()</span></code> method. This includes the situations where attributes are
|
||
inherited from a superclass.</li>
|
||
</ul>
|
||
<p>In addition, a systematic effort will be put into going through
|
||
existing standard library classes and adding <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> where
|
||
it looks beneficial.</p>
|
||
</section>
|
||
</section>
|
||
<section id="static-checkers-specification">
|
||
<h2><a class="toc-backref" href="#static-checkers-specification" role="doc-backlink">Static checkers specification</a></h2>
|
||
<section id="exhaustiveness-checks">
|
||
<h3><a class="toc-backref" href="#exhaustiveness-checks" role="doc-backlink">Exhaustiveness checks</a></h3>
|
||
<p>From a reliability perspective, experience shows that missing a case when
|
||
dealing with a set of possible data values leads to hard to debug issues,
|
||
thus forcing people to add safety asserts like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">get_first</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]])</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nb">list</span><span class="p">)</span> <span class="ow">and</span> <span class="n">data</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||
<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nb">int</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">data</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">assert</span> <span class="kc">False</span><span class="p">,</span> <span class="s2">"should never get here"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> specifies that static type checkers should support exhaustiveness in
|
||
conditional checks with respect to enum values. <a class="pep reference internal" href="../pep-0586/" title="PEP 586 – Literal Types">PEP 586</a> later generalized this
|
||
requirement to literal types.</p>
|
||
<p>This PEP further generalizes this requirement to
|
||
arbitrary patterns. A typical situation where this applies is matching an
|
||
expression with a union type:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">classify</span><span class="p">(</span><span class="n">val</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">],</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]])</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="k">match</span> <span class="n">val</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]</span> <span class="k">if</span> <span class="n">x</span> <span class="o">></span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"A pair of </span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s2"> and </span><span class="si">{</span><span class="n">y</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="o">*</span><span class="n">other</span><span class="p">]:</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"A sequence starting with </span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="k">case</span> <span class="nb">int</span><span class="p">():</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"Some integer"</span>
|
||
<span class="c1"># Type-checking error: some cases unhandled.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The exhaustiveness checks should also apply where both pattern matching
|
||
and enum values are combined:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span>
|
||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Union</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Level</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||
<span class="n">BASIC</span> <span class="o">=</span> <span class="mi">1</span>
|
||
<span class="n">ADVANCED</span> <span class="o">=</span> <span class="mi">2</span>
|
||
<span class="n">PRO</span> <span class="o">=</span> <span class="mi">3</span>
|
||
|
||
<span class="k">class</span> <span class="nc">User</span><span class="p">:</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">level</span><span class="p">:</span> <span class="n">Level</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Admin</span><span class="p">:</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
||
|
||
<span class="n">account</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n">User</span><span class="p">,</span> <span class="n">Admin</span><span class="p">]</span>
|
||
|
||
<span class="k">match</span> <span class="n">account</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">Admin</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">)</span> <span class="o">|</span> <span class="n">User</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">,</span> <span class="n">level</span><span class="o">=</span><span class="n">Level</span><span class="o">.</span><span class="n">PRO</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">User</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">Level</span><span class="o">.</span><span class="n">ADVANCED</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
<span class="c1"># Type-checking error: basic user unhandled</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Obviously, no <code class="docutils literal notranslate"><span class="pre">Matchable</span></code> protocol (in terms of <a class="pep reference internal" href="../pep-0544/" title="PEP 544 – Protocols: Structural subtyping (static duck typing)">PEP 544</a>) is needed, since
|
||
every class is matchable and therefore is subject to the checks specified
|
||
above.</p>
|
||
</section>
|
||
<section id="sealed-classes-as-algebraic-data-types">
|
||
<h3><a class="toc-backref" href="#sealed-classes-as-algebraic-data-types" role="doc-backlink">Sealed classes as algebraic data types</a></h3>
|
||
<p>Quite often it is desirable to apply exhaustiveness to a set of classes without
|
||
defining ad-hoc union types, which is itself fragile if a class is missing in
|
||
the union definition. A design pattern where a group of record-like classes is
|
||
combined into a union is popular in other languages that support pattern
|
||
matching and is known under a name of <a class="reference external" href="https://en.wikipedia.org/wiki/Algebraic_data_type">algebraic data types</a>.</p>
|
||
<p>We propose to add a special decorator class <code class="docutils literal notranslate"><span class="pre">@sealed</span></code> to the <a class="reference external" href="https://docs.python.org/3/library/typing.html#module-typing" title="(in Python v3.12)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">typing</span></code></a>
|
||
module, that will have no effect at runtime, but will indicate to static
|
||
type checkers that all subclasses (direct and indirect) of this class should
|
||
be defined in the same module as the base class.</p>
|
||
<p>The idea is that since all subclasses are known, the type checker can treat
|
||
the sealed base class as a union of all its subclasses. Together with
|
||
dataclasses this allows a clean and safe support of algebraic data types
|
||
in Python. Consider this example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
|
||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">sealed</span>
|
||
|
||
<span class="nd">@sealed</span>
|
||
<span class="k">class</span> <span class="nc">Node</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Expression</span><span class="p">(</span><span class="n">Node</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Statement</span><span class="p">(</span><span class="n">Node</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="nd">@dataclass</span>
|
||
<span class="k">class</span> <span class="nc">Name</span><span class="p">(</span><span class="n">Expression</span><span class="p">):</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
||
|
||
<span class="nd">@dataclass</span>
|
||
<span class="k">class</span> <span class="nc">Operation</span><span class="p">(</span><span class="n">Expression</span><span class="p">):</span>
|
||
<span class="n">left</span><span class="p">:</span> <span class="n">Expression</span>
|
||
<span class="n">op</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">right</span><span class="p">:</span> <span class="n">Expression</span>
|
||
|
||
<span class="nd">@dataclass</span>
|
||
<span class="k">class</span> <span class="nc">Assignment</span><span class="p">(</span><span class="n">Statement</span><span class="p">):</span>
|
||
<span class="n">target</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">value</span><span class="p">:</span> <span class="n">Expression</span>
|
||
|
||
<span class="nd">@dataclass</span>
|
||
<span class="k">class</span> <span class="nc">Print</span><span class="p">(</span><span class="n">Statement</span><span class="p">):</span>
|
||
<span class="n">value</span><span class="p">:</span> <span class="n">Expression</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>With such definition, a type checker can safely treat <code class="docutils literal notranslate"><span class="pre">Node</span></code> as
|
||
<code class="docutils literal notranslate"><span class="pre">Union[Name,</span> <span class="pre">Operation,</span> <span class="pre">Assignment,</span> <span class="pre">Print]</span></code>, and also safely treat e.g.
|
||
<code class="docutils literal notranslate"><span class="pre">Expression</span></code> as <code class="docutils literal notranslate"><span class="pre">Union[Name,</span> <span class="pre">Operation]</span></code>. So this will result in a type
|
||
checking error in the below snippet, because <code class="docutils literal notranslate"><span class="pre">Name</span></code> is not handled (and type
|
||
checker can give a useful error message):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dump</span><span class="p">(</span><span class="n">node</span><span class="p">:</span> <span class="n">Node</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="k">match</span> <span class="n">node</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">Assignment</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2"> = </span><span class="si">{</span><span class="n">dump</span><span class="p">(</span><span class="n">value</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="k">case</span> <span class="n">Print</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"print(</span><span class="si">{</span><span class="n">dump</span><span class="p">(</span><span class="n">value</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
||
<span class="k">case</span> <span class="n">Operation</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">right</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="sa">f</span><span class="s2">"(</span><span class="si">{</span><span class="n">dump</span><span class="p">(</span><span class="n">left</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">op</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">dump</span><span class="p">(</span><span class="n">right</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="type-erasure">
|
||
<h3><a class="toc-backref" href="#type-erasure" role="doc-backlink">Type erasure</a></h3>
|
||
<p>Class patterns are subject to runtime type erasure. Namely, although one
|
||
can define a type alias <code class="docutils literal notranslate"><span class="pre">IntQueue</span> <span class="pre">=</span> <span class="pre">Queue[int]</span></code> so that a pattern like
|
||
<code class="docutils literal notranslate"><span class="pre">IntQueue()</span></code> is syntactically valid, type checkers should reject such a
|
||
match:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">queue</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n">Queue</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">Queue</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span>
|
||
<span class="k">match</span> <span class="n">queue</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">IntQueue</span><span class="p">():</span> <span class="c1"># Type-checking error here</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that the above snippet actually fails at runtime with the current
|
||
implementation of generic classes in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module, as well as
|
||
with builtin generic classes in the recently accepted <a class="pep reference internal" href="../pep-0585/" title="PEP 585 – Type Hinting Generics In Standard Collections">PEP 585</a>, because
|
||
they prohibit <code class="docutils literal notranslate"><span class="pre">isinstance</span></code> checks.</p>
|
||
<p>To clarify, generic classes are not prohibited in general from participating
|
||
in pattern matching, just that their type parameters can’t be explicitly
|
||
specified. It is still fine if sub-patterns or literals bind the type
|
||
variables. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Generic</span><span class="p">,</span> <span class="n">TypeVar</span><span class="p">,</span> <span class="n">Union</span>
|
||
|
||
<span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">)</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Result</span><span class="p">(</span><span class="n">Generic</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
||
<span class="n">first</span><span class="p">:</span> <span class="n">T</span>
|
||
<span class="n">other</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">T</span><span class="p">]</span>
|
||
|
||
<span class="n">result</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n">Result</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">Result</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span>
|
||
|
||
<span class="k">match</span> <span class="n">result</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">Result</span><span class="p">(</span><span class="n">first</span><span class="o">=</span><span class="nb">int</span><span class="p">()):</span>
|
||
<span class="o">...</span> <span class="c1"># Type of result is Result[int] here</span>
|
||
<span class="k">case</span> <span class="n">Result</span><span class="p">(</span><span class="n">other</span><span class="o">=</span><span class="p">[</span><span class="s2">"foo"</span><span class="p">,</span> <span class="s2">"bar"</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="p">]):</span>
|
||
<span class="o">...</span> <span class="c1"># Type of result is Result[str] here</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="note-about-constants">
|
||
<h3><a class="toc-backref" href="#note-about-constants" role="doc-backlink">Note about constants</a></h3>
|
||
<p>The fact that a capture pattern is always an assignment target may create unwanted
|
||
consequences when a user by mistake tries to “match” a value against
|
||
a constant instead of using the constant value pattern. As a result, at
|
||
runtime such a match will always succeed and moreover override the value of
|
||
the constant. It is important therefore that static type checkers warn about
|
||
such situations. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Final</span>
|
||
|
||
<span class="n">MAX_INT</span><span class="p">:</span> <span class="n">Final</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">64</span>
|
||
|
||
<span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="k">match</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">MAX_INT</span><span class="p">:</span> <span class="c1"># Type-checking error here: cannot assign to final name</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Got big number"</span><span class="p">)</span>
|
||
<span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Something else"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that the CPython reference implementation also generates a
|
||
<code class="docutils literal notranslate"><span class="pre">SyntaxWarning</span></code> message for this case.</p>
|
||
</section>
|
||
<section id="precise-type-checking-of-star-matches">
|
||
<h3><a class="toc-backref" href="#precise-type-checking-of-star-matches" role="doc-backlink">Precise type checking of star matches</a></h3>
|
||
<p>Type checkers should perform precise type checking of star items in pattern
|
||
matching giving them either a heterogeneous <code class="docutils literal notranslate"><span class="pre">list[T]</span></code> type, or
|
||
a <code class="docutils literal notranslate"><span class="pre">TypedDict</span></code> type as specified by <a class="pep reference internal" href="../pep-0589/" title="PEP 589 – TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys">PEP 589</a>. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">,</span> <span class="nb">float</span><span class="p">]</span>
|
||
|
||
<span class="k">match</span> <span class="n">stuff</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">:</span>
|
||
<span class="c1"># Here a is int and b is list[str]</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="performance-considerations">
|
||
<h2><a class="toc-backref" href="#performance-considerations" role="doc-backlink">Performance Considerations</a></h2>
|
||
<p>Ideally, a <code class="docutils literal notranslate"><span class="pre">match</span></code> statement should have good runtime performance compared
|
||
to an equivalent chain of if-statements. Although the history of programming
|
||
languages is rife with examples of new features which increased engineer
|
||
productivity at the expense of additional CPU cycles, it would be
|
||
unfortunate if the benefits of <code class="docutils literal notranslate"><span class="pre">match</span></code> were counter-balanced by a significant
|
||
overall decrease in runtime performance.</p>
|
||
<p>Although this PEP does not specify any particular implementation strategy,
|
||
a few words about the prototype implementation and how it attempts to
|
||
maximize performance are in order.</p>
|
||
<p>Basically, the prototype implementation transforms all of the <code class="docutils literal notranslate"><span class="pre">match</span></code>
|
||
statement syntax into equivalent if/else blocks - or more accurately, into
|
||
Python byte codes that have the same effect. In other words, all of the
|
||
logic for testing instance types, sequence lengths, mapping keys and
|
||
so on are inlined in place of the <code class="docutils literal notranslate"><span class="pre">match</span></code>.</p>
|
||
<p>This is not the only possible strategy, nor is it necessarily the best.
|
||
For example, the instance checks could be memoized, especially
|
||
if there are multiple instances of the same class type but with different
|
||
arguments in a single match statement. It is also theoretically
|
||
possible for a future implementation to process case clauses or sub-patterns in
|
||
parallel using a decision tree rather than testing them one by one.</p>
|
||
</section>
|
||
<section id="backwards-compatibility">
|
||
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
|
||
<p>This PEP is fully backwards compatible: the <code class="docutils literal notranslate"><span class="pre">match</span></code> and <code class="docutils literal notranslate"><span class="pre">case</span></code>
|
||
keywords are proposed to be (and stay!) soft keywords, so their use as
|
||
variable, function, class, module or attribute names is not impeded at
|
||
all.</p>
|
||
<p>This is important because <code class="docutils literal notranslate"><span class="pre">match</span></code> is the name of a popular and
|
||
well-known function and method in the <code class="docutils literal notranslate"><span class="pre">re</span></code> module, which we have no
|
||
desire to break or deprecate.</p>
|
||
<p>The difference between hard and soft keywords is that hard keywords
|
||
are <em>always</em> reserved words, even in positions where they make no
|
||
sense (e.g. <code class="docutils literal notranslate"><span class="pre">x</span> <span class="pre">=</span> <span class="pre">class</span> <span class="pre">+</span> <span class="pre">1</span></code>), while soft keywords only get a special
|
||
meaning in context. Since <a class="pep reference internal" href="../pep-0617/" title="PEP 617 – New PEG parser for CPython">PEP 617</a> the parser backtracks, that means that on
|
||
different attempts to parse a code fragment it could interpret a soft
|
||
keyword differently.</p>
|
||
<p>For example, suppose the parser encounters the following input:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="p">[</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">]:</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The parser first attempts to parse this as an expression statement.
|
||
It interprets <code class="docutils literal notranslate"><span class="pre">match</span></code> as a NAME token, and then considers <code class="docutils literal notranslate"><span class="pre">[x,</span>
|
||
<span class="pre">y]</span></code> to be a double subscript. It then encounters the colon and has
|
||
to backtrack, since an expression statement cannot be followed by a
|
||
colon. The parser then backtracks to the start of the line and finds
|
||
that <code class="docutils literal notranslate"><span class="pre">match</span></code> is a soft keyword allowed in this position. It then
|
||
considers <code class="docutils literal notranslate"><span class="pre">[x,</span> <span class="pre">y]</span></code> to be a list expression. The colon then is just
|
||
what the parser expected, and the parse succeeds.</p>
|
||
</section>
|
||
<section id="impacts-on-third-party-tools">
|
||
<h2><a class="toc-backref" href="#impacts-on-third-party-tools" role="doc-backlink">Impacts on third-party tools</a></h2>
|
||
<p>There are a lot of tools in the Python ecosystem that operate on Python
|
||
source code: linters, syntax highlighters, auto-formatters, and IDEs. These
|
||
will all need to be updated to include awareness of the <code class="docutils literal notranslate"><span class="pre">match</span></code> statement.</p>
|
||
<p>In general, these tools fall into one of two categories:</p>
|
||
<p><strong>Shallow</strong> parsers don’t try to understand the full syntax of Python, but
|
||
instead scan the source code for specific known patterns. IDEs, such as Visual
|
||
Studio Code, Emacs and TextMate, tend to fall in this category, since frequently
|
||
the source code is invalid while being edited, and a strict approach to parsing
|
||
would fail.</p>
|
||
<p>For these kinds of tools, adding knowledge of a new keyword is relatively
|
||
easy, just an addition to a table, or perhaps modification of a regular
|
||
expression.</p>
|
||
<p><strong>Deep</strong> parsers understand the complete syntax of Python. An example of this
|
||
is the auto-formatter <a class="reference external" href="https://black.readthedocs.io/en/stable/">Black</a>. A particular requirement with these kinds of
|
||
tools is that they not only need to understand the syntax of the current version
|
||
of Python, but older versions of Python as well.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">match</span></code> statement uses a soft keyword, and it is one of the first major
|
||
Python features to take advantage of the capabilities of the new PEG parser. This
|
||
means that third-party parsers which are not ‘PEG-compatible’ will have a hard
|
||
time with the new syntax.</p>
|
||
<p>It has been noted that a number of these third-party tools leverage common parsing
|
||
libraries (Black for example uses a fork of the lib2to3 parser). It may be helpful
|
||
to identify widely used parsing libraries (such as <a class="reference external" href="https://github.com/davidhalter/parso">parso</a> and <a class="reference external" href="https://github.com/Instagram/LibCST">libCST</a>)
|
||
and upgrade them to be PEG compatible.</p>
|
||
<p>However, since this work would need to be done not only for the match statement,
|
||
but for <em>any</em> new Python syntax that leverages the capabilities of the PEG parser,
|
||
it is considered out of scope for this PEP. (Although it is suggested that this
|
||
would make a fine Summer of Code project.)</p>
|
||
</section>
|
||
<section id="reference-implementation">
|
||
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
|
||
<p>A <a class="reference external" href="https://github.com/brandtbucher/cpython/tree/patma">feature-complete CPython implementation</a> is available on
|
||
GitHub.</p>
|
||
<p>An <a class="reference external" href="https://mybinder.org/v2/gh/gvanrossum/patma/master?urlpath=lab/tree/playground-622.ipynb">interactive playground</a>
|
||
based on the above implementation was created using <a class="reference external" href="https://mybinder.org">Binder</a> and <a class="reference external" href="https://jupyter.org">Jupyter</a>.</p>
|
||
</section>
|
||
<section id="example-code">
|
||
<h2><a class="toc-backref" href="#example-code" role="doc-backlink">Example Code</a></h2>
|
||
<p>A small <a class="reference external" href="https://github.com/gvanrossum/patma/tree/master/examples">collection of example code</a> is
|
||
available on GitHub.</p>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
|
||
<p>This general idea has been floating around for a pretty long time, and many
|
||
back and forth decisions were made. Here we summarize many alternative
|
||
paths that were taken but eventually abandoned.</p>
|
||
<section id="don-t-do-this-pattern-matching-is-hard-to-learn">
|
||
<h3><a class="toc-backref" href="#don-t-do-this-pattern-matching-is-hard-to-learn" role="doc-backlink">Don’t do this, pattern matching is hard to learn</a></h3>
|
||
<p>In our opinion, the proposed pattern matching is not more difficult than
|
||
adding <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> and <code class="docutils literal notranslate"><span class="pre">getattr()</span></code> to iterable unpacking. Also, we
|
||
believe the proposed syntax significantly improves readability for a wide
|
||
range of code patterns, by allowing to express <em>what</em> one wants to do, rather
|
||
than <em>how</em> to do it. We hope the few real code snippets we included in the PEP
|
||
above illustrate this comparison well enough. For more real code examples
|
||
and their translations see Ref. <a class="footnote-reference brackets" href="#id3" id="id2">[1]</a>.</p>
|
||
</section>
|
||
<section id="don-t-do-this-use-existing-method-dispatching-mechanisms">
|
||
<h3><a class="toc-backref" href="#don-t-do-this-use-existing-method-dispatching-mechanisms" role="doc-backlink">Don’t do this, use existing method dispatching mechanisms</a></h3>
|
||
<p>We recognize that some of the use cases for the <code class="docutils literal notranslate"><span class="pre">match</span></code> statement overlap
|
||
with what can be done with traditional object-oriented programming (OOP) design
|
||
techniques using class inheritance. The ability to choose alternate
|
||
behaviors based on testing the runtime type of a match subject might
|
||
even seem heretical to strict OOP purists.</p>
|
||
<p>However, Python has always been a language that embraces a variety of
|
||
programming styles and paradigms. Classic Python design idioms such as
|
||
“duck”-typing go beyond the traditional OOP model.</p>
|
||
<p>We believe that there are important use cases where the use of <code class="docutils literal notranslate"><span class="pre">match</span></code> results
|
||
in a cleaner and more maintainable architecture. These use cases tend to
|
||
be characterized by a number of features:</p>
|
||
<ul class="simple">
|
||
<li>Algorithms which cut across traditional lines of data encapsulation. If an
|
||
algorithm is processing heterogeneous elements of different types (such as
|
||
evaluating or transforming an abstract syntax tree, or doing algebraic
|
||
manipulation of mathematical symbols), forcing the user to implement
|
||
the algorithm as individual methods on each element type results in
|
||
logic that is smeared across the entire codebase instead of being neatly
|
||
localized in one place.</li>
|
||
<li>Program architectures where the set of possible data types is relatively
|
||
stable, but there is an ever-expanding set of operations to be performed
|
||
on those data types. Doing this in a strict OOP fashion requires constantly
|
||
adding new methods to both the base class and subclasses to support the new
|
||
methods, “polluting” the base class with lots of very specialized method
|
||
definitions, and causing widespread disruption and churn in the code. By
|
||
contrast, in a <code class="docutils literal notranslate"><span class="pre">match</span></code>-based dispatch, adding a new behavior merely
|
||
involves writing a new <code class="docutils literal notranslate"><span class="pre">match</span></code> statement.</li>
|
||
<li>OOP also does not handle dispatching based on the <em>shape</em> of an object, such
|
||
as the length of a tuple, or the presence of an attribute – instead any such
|
||
dispatching decision must be encoded into the object’s type. Shape-based
|
||
dispatching is particularly interesting when it comes to handling “duck”-typed
|
||
objects.</li>
|
||
</ul>
|
||
<p>Where OOP is clearly superior is in the opposite case: where the set of possible
|
||
operations is relatively stable and well-defined, but there is an ever-growing
|
||
set of data types to operate on. A classic example of this is UI widget toolkits,
|
||
where there is a fixed set of interaction types (repaint, mouse click, keypress,
|
||
and so on), but the set of widget types is constantly expanding as developers
|
||
invent new and creative user interaction styles. Adding a new kind of widget
|
||
is a simple matter of writing a new subclass, whereas with a match-based approach
|
||
you end up having to add a new case clause to many widespread match statements.
|
||
We therefore don’t recommend using <code class="docutils literal notranslate"><span class="pre">match</span></code> in such a situation.</p>
|
||
</section>
|
||
<section id="allow-more-flexible-assignment-targets-instead">
|
||
<h3><a class="toc-backref" href="#allow-more-flexible-assignment-targets-instead" role="doc-backlink">Allow more flexible assignment targets instead</a></h3>
|
||
<p>There was an idea to instead just generalize the iterable unpacking to much
|
||
more general assignment targets, instead of adding a new kind of statement.
|
||
This concept is known in some other languages as “irrefutable matches”. We
|
||
decided not to do this because inspection of real-life potential use cases
|
||
showed that in vast majority of cases destructuring is related to an <code class="docutils literal notranslate"><span class="pre">if</span></code>
|
||
condition. Also many of those are grouped in a series of exclusive choices.</p>
|
||
</section>
|
||
<section id="make-it-an-expression">
|
||
<h3><a class="toc-backref" href="#make-it-an-expression" role="doc-backlink">Make it an expression</a></h3>
|
||
<p>In most other languages pattern matching is represented by an expression, not
|
||
statement. But making it an expression would be inconsistent with other
|
||
syntactic choices in Python. All decision making logic is expressed almost
|
||
exclusively in statements, so we decided to not deviate from this.</p>
|
||
</section>
|
||
<section id="use-a-hard-keyword">
|
||
<h3><a class="toc-backref" href="#use-a-hard-keyword" role="doc-backlink">Use a hard keyword</a></h3>
|
||
<p>There were options to make <code class="docutils literal notranslate"><span class="pre">match</span></code> a hard keyword, or choose a different
|
||
keyword. Although using a hard keyword would simplify life for simple-minded
|
||
syntax highlighters, we decided not to use hard keyword for several reasons:</p>
|
||
<ul class="simple">
|
||
<li>Most importantly, the new parser doesn’t require us to do this. Unlike with
|
||
<code class="docutils literal notranslate"><span class="pre">async</span></code> that caused hardships with being a soft keyword for few releases,
|
||
here we can make <code class="docutils literal notranslate"><span class="pre">match</span></code> a permanent soft keyword.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">match</span></code> is so commonly used in existing code, that it would break almost
|
||
every existing program and will put a burden to fix code on many people who
|
||
may not even benefit from the new syntax.</li>
|
||
<li>It is hard to find an alternative keyword that would not be commonly used
|
||
in existing programs as an identifier, and would still clearly reflect the
|
||
meaning of the statement.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="use-as-or-instead-of-case-for-case-clauses">
|
||
<h3><a class="toc-backref" href="#use-as-or-instead-of-case-for-case-clauses" role="doc-backlink">Use <code class="docutils literal notranslate"><span class="pre">as</span></code> or <code class="docutils literal notranslate"><span class="pre">|</span></code> instead of <code class="docutils literal notranslate"><span class="pre">case</span></code> for case clauses</a></h3>
|
||
<p>The pattern matching proposed here is a combination of multi-branch control
|
||
flow (in line with <code class="docutils literal notranslate"><span class="pre">switch</span></code> in Algol-derived languages or <code class="docutils literal notranslate"><span class="pre">cond</span></code> in Lisp)
|
||
and object-deconstruction as found in functional languages. While the proposed
|
||
keyword <code class="docutils literal notranslate"><span class="pre">case</span></code> highlights the multi-branch aspect, alternative keywords such
|
||
as <code class="docutils literal notranslate"><span class="pre">as</span></code> would equally be possible, highlighting the deconstruction aspect.
|
||
<code class="docutils literal notranslate"><span class="pre">as</span></code> or <code class="docutils literal notranslate"><span class="pre">with</span></code>, for instance, also have the advantage of already being
|
||
keywords in Python. However, since <code class="docutils literal notranslate"><span class="pre">case</span></code> as a keyword can only occur as a
|
||
leading keyword inside a <code class="docutils literal notranslate"><span class="pre">match</span></code> statement, it is easy for a parser to
|
||
distinguish between its use as a keyword or as a variable.</p>
|
||
<p>Other variants would use a symbol like <code class="docutils literal notranslate"><span class="pre">|</span></code> or <code class="docutils literal notranslate"><span class="pre">=></span></code>, or go entirely without
|
||
special marker.</p>
|
||
<p>Since Python is a statement-oriented language in the tradition of Algol, and as
|
||
each composite statement starts with an identifying keyword, <code class="docutils literal notranslate"><span class="pre">case</span></code> seemed to
|
||
be most in line with Python’s style and traditions.</p>
|
||
</section>
|
||
<section id="use-a-flat-indentation-scheme">
|
||
<h3><a class="toc-backref" href="#use-a-flat-indentation-scheme" role="doc-backlink">Use a flat indentation scheme</a></h3>
|
||
<p>There was an idea to use an alternative indentation scheme, for example where
|
||
every case clause would not be indented with respect to the initial <code class="docutils literal notranslate"><span class="pre">match</span></code>
|
||
part:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">expression</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">pattern_1</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">pattern_2</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The motivation is that although flat indentation saves some horizontal space,
|
||
it may look awkward to an eye of a Python programmer, because everywhere else
|
||
colon is followed by an indent. This will also complicate life for
|
||
simple-minded code editors. Finally, the horizontal space issue can be
|
||
alleviated by allowing “half-indent” (i.e. two spaces instead of four) for
|
||
match statements.</p>
|
||
<p>In sample programs using <code class="docutils literal notranslate"><span class="pre">match</span></code>, written as part of the development of this
|
||
PEP, a noticeable improvement in code brevity is observed, more than making up
|
||
for the additional indentation level.</p>
|
||
<p>Another proposal considered was to use flat indentation but put the
|
||
expression on the line after <code class="docutils literal notranslate"><span class="pre">match:</span></code>, like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">match</span><span class="p">:</span>
|
||
<span class="n">expression</span>
|
||
<span class="k">case</span> <span class="n">pattern_1</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">case</span> <span class="n">pattern_2</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This was ultimately rejected because the first block would be a
|
||
novelty in Python’s grammar: a block whose only content is a single
|
||
expression rather than a sequence of statements.</p>
|
||
</section>
|
||
<section id="alternatives-for-constant-value-pattern">
|
||
<h3><a class="toc-backref" href="#alternatives-for-constant-value-pattern" role="doc-backlink">Alternatives for constant value pattern</a></h3>
|
||
<p>This is probably the trickiest item. Matching against some pre-defined
|
||
constants is very common, but the dynamic nature of Python also makes it
|
||
ambiguous with capture patterns. Five other alternatives were considered:</p>
|
||
<ul>
|
||
<li>Use some implicit rules. For example, if a name was defined in the global
|
||
scope, then it refers to a constant, rather than representing a
|
||
capture pattern:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Here, the name "spam" must be defined in the global scope (and</span>
|
||
<span class="c1"># not shadowed locally). "side" must be local.</span>
|
||
|
||
<span class="k">match</span> <span class="n">entree</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
|
||
<span class="k">case</span> <span class="n">spam</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Compares entree[-1] == spam.</span>
|
||
<span class="k">case</span> <span class="n">side</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Assigns side = entree[-1].</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This however can cause surprises and action at a distance if someone
|
||
defines an unrelated coinciding name before the match statement.</p>
|
||
</li>
|
||
<li>Use a rule based on the case of a name. In particular, if the name
|
||
starts with a lowercase letter it would be a capture pattern, while if
|
||
it starts with uppercase it would refer to a constant:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">entree</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
|
||
<span class="k">case</span> <span class="n">SPAM</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Compares entree[-1] == SPAM.</span>
|
||
<span class="k">case</span> <span class="n">side</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Assigns side = entree[-1].</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This works well with the recommendations for naming constants from
|
||
<a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a>. The main objection is that there’s no other part of core
|
||
Python where the case of a name is semantically significant.
|
||
In addition, Python allows identifiers to use different scripts,
|
||
many of which (e.g. CJK) don’t have a case distinction.</p>
|
||
</li>
|
||
<li>Use extra parentheses to indicate lookup semantics for a given name. For
|
||
example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">entree</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
|
||
<span class="k">case</span> <span class="p">(</span><span class="n">spam</span><span class="p">):</span> <span class="o">...</span> <span class="c1"># Compares entree[-1] == spam.</span>
|
||
<span class="k">case</span> <span class="n">side</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Assigns side = entree[-1].</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This may be a viable option, but it can create some visual noise if used
|
||
often. Also honestly it looks pretty unusual, especially in nested contexts.</p>
|
||
<p>This also has the problem that we may want or need parentheses to
|
||
disambiguate grouping in patterns, e.g. in <code class="docutils literal notranslate"><span class="pre">Point(x,</span> <span class="pre">y=(y</span> <span class="pre">:=</span>
|
||
<span class="pre">complex()))</span></code>.</p>
|
||
</li>
|
||
<li>Introduce a special symbol, for example <code class="docutils literal notranslate"><span class="pre">.</span></code>, <code class="docutils literal notranslate"><span class="pre">?</span></code>, <code class="docutils literal notranslate"><span class="pre">$</span></code>, or <code class="docutils literal notranslate"><span class="pre">^</span></code> to
|
||
indicate that a given name is a value to be matched against, not
|
||
to be assigned to. An earlier version of this proposal used a
|
||
leading-dot rule:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">entree</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="n">spam</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Compares entree[-1] == spam.</span>
|
||
<span class="k">case</span> <span class="n">side</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Assigns side = entree[-1].</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>While potentially useful, it introduces strange-looking new syntax
|
||
without making the pattern syntax any more expressive. Indeed,
|
||
named constants can be made to work with the existing rules by
|
||
converting them to <code class="docutils literal notranslate"><span class="pre">Enum</span></code> types, or enclosing them in their own
|
||
namespace (considered by the authors to be one honking great idea):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">entree</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
|
||
<span class="k">case</span> <span class="n">Sides</span><span class="o">.</span><span class="n">SPAM</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Compares entree[-1] == Sides.SPAM.</span>
|
||
<span class="k">case</span> <span class="n">side</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Assigns side = entree[-1].</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If needed, the leading-dot rule (or a similar variant) could be
|
||
added back later with no backward-compatibility issues.</p>
|
||
</li>
|
||
<li>There was also an idea to make lookup semantics the default, and require
|
||
<code class="docutils literal notranslate"><span class="pre">$</span></code> or <code class="docutils literal notranslate"><span class="pre">?</span></code> to be used in capture patterns:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>match entree[-1]:
|
||
case spam: ... # Compares entree[-1] == spam.
|
||
case side?: ... # Assigns side = entree[-1].
|
||
</pre></div>
|
||
</div>
|
||
<p>There are a few issues with this:</p>
|
||
<ul>
|
||
<li>Capture patterns are more common in typical code, so it is
|
||
undesirable to require special syntax for them.</li>
|
||
<li>The authors are not aware of any other language that adorns
|
||
captures in this way.</li>
|
||
<li>None of the proposed syntaxes have any precedent in Python;
|
||
no other place in Python that binds names (e.g. <code class="docutils literal notranslate"><span class="pre">import</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">def</span></code>, <code class="docutils literal notranslate"><span class="pre">for</span></code>) uses special marker syntax.</li>
|
||
<li>It would break the syntactic parallels of the current grammar:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>match coords:
|
||
case ($x, $y):
|
||
return Point(x, y) # Why not "Point($x, $y)"?
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<p>In the end, these alternatives were rejected because of the mentioned drawbacks.</p>
|
||
</section>
|
||
<section id="disallow-float-literals-in-patterns">
|
||
<h3><a class="toc-backref" href="#disallow-float-literals-in-patterns" role="doc-backlink">Disallow float literals in patterns</a></h3>
|
||
<p>Because of the inexactness of floats, an early version of this proposal
|
||
did not allow floating-point constants to be used as match patterns. Part
|
||
of the justification for this prohibition is that Rust does this.</p>
|
||
<p>However, during implementation, it was discovered that distinguishing between
|
||
float values and other types required extra code in the VM that would slow
|
||
matches generally. Given that Python and Rust are very different languages
|
||
with different user bases and underlying philosophies, it was felt that
|
||
allowing float literals would not cause too much harm, and would be less
|
||
surprising to users.</p>
|
||
</section>
|
||
<section id="range-matching-patterns">
|
||
<h3><a class="toc-backref" href="#range-matching-patterns" role="doc-backlink">Range matching patterns</a></h3>
|
||
<p>This would allow patterns such as <code class="docutils literal notranslate"><span class="pre">1...6</span></code>. However, there are a host of
|
||
ambiguities:</p>
|
||
<ul class="simple">
|
||
<li>Is the range open, half-open, or closed? (I.e. is <code class="docutils literal notranslate"><span class="pre">6</span></code> included in the
|
||
above example or not?)</li>
|
||
<li>Does the range match a single number, or a range object?</li>
|
||
<li>Range matching is often used for character ranges (‘a’…’z’) but that
|
||
won’t work in Python since there’s no character data type, just strings.</li>
|
||
<li>Range matching can be a significant performance optimization if you can
|
||
pre-build a jump table, but that’s not generally possible in Python due
|
||
to the fact that names can be dynamically rebound.</li>
|
||
</ul>
|
||
<p>Rather than creating a special-case syntax for ranges, it was decided
|
||
that allowing custom pattern objects (<code class="docutils literal notranslate"><span class="pre">InRange(0,</span> <span class="pre">6)</span></code>) would be more flexible
|
||
and less ambiguous; however those ideas have been postponed for the time
|
||
being (See <a class="reference internal" href="#deferred-ideas">deferred ideas</a>).</p>
|
||
</section>
|
||
<section id="use-dispatch-dict-semantics-for-matches">
|
||
<h3><a class="toc-backref" href="#use-dispatch-dict-semantics-for-matches" role="doc-backlink">Use dispatch dict semantics for matches</a></h3>
|
||
<p>Implementations for classic <code class="docutils literal notranslate"><span class="pre">switch</span></code> statement sometimes use a pre-computed
|
||
hash table instead of a chained equality comparisons to gain some performance.
|
||
In the context of <code class="docutils literal notranslate"><span class="pre">match</span></code> statement this is technically also possible for
|
||
matches against literal patterns. However, having subtly different semantics
|
||
for different kinds of patterns would be too surprising for potentially
|
||
modest performance win.</p>
|
||
<p>We can still experiment with possible performance optimizations in this
|
||
direction if they will not cause semantic differences.</p>
|
||
</section>
|
||
<section id="use-continue-and-break-in-case-clauses">
|
||
<h3><a class="toc-backref" href="#use-continue-and-break-in-case-clauses" role="doc-backlink">Use <code class="docutils literal notranslate"><span class="pre">continue</span></code> and <code class="docutils literal notranslate"><span class="pre">break</span></code> in case clauses.</a></h3>
|
||
<p>Another rejected proposal was to define new meanings for <code class="docutils literal notranslate"><span class="pre">continue</span></code>
|
||
and <code class="docutils literal notranslate"><span class="pre">break</span></code> inside of <code class="docutils literal notranslate"><span class="pre">match</span></code>, which would have the following behavior:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">continue</span></code> would exit the current case clause and continue matching
|
||
at the next case clause.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">break</span></code> would exit the match statement.</li>
|
||
</ul>
|
||
<p>However, there is a serious drawback to this proposal: if the <code class="docutils literal notranslate"><span class="pre">match</span></code> statement
|
||
is nested inside of a loop, the meanings of <code class="docutils literal notranslate"><span class="pre">continue</span></code> and <code class="docutils literal notranslate"><span class="pre">break</span></code> are now
|
||
changed. This may cause unexpected behavior during refactorings; also, an
|
||
argument can be made that there are other means to get the same behavior (such
|
||
as using guard conditions), and that in practice it’s likely that the existing
|
||
behavior of <code class="docutils literal notranslate"><span class="pre">continue</span></code> and <code class="docutils literal notranslate"><span class="pre">break</span></code> are far more useful.</p>
|
||
</section>
|
||
<section id="and-patterns">
|
||
<h3><a class="toc-backref" href="#and-patterns" role="doc-backlink">AND (<code class="docutils literal notranslate"><span class="pre">&</span></code>) patterns</a></h3>
|
||
<p>This proposal defines an OR-pattern (<code class="docutils literal notranslate"><span class="pre">|</span></code>) to match one of several alternates;
|
||
why not also an AND-pattern (<code class="docutils literal notranslate"><span class="pre">&</span></code>)? Especially given that some other languages
|
||
(F# for example) support this.</p>
|
||
<p>However, it’s not clear how useful this would be. The semantics for matching
|
||
dictionaries, objects and sequences already incorporates an implicit ‘and’: all
|
||
attributes and elements mentioned must be present for the match to succeed. Guard
|
||
conditions can also support many of the use cases that a hypothetical ‘and’
|
||
operator would be used for.</p>
|
||
<p>In the end, it was decided that this would make the syntax more complex without
|
||
adding a significant benefit.</p>
|
||
</section>
|
||
<section id="negative-match-patterns">
|
||
<h3><a class="toc-backref" href="#negative-match-patterns" role="doc-backlink">Negative match patterns</a></h3>
|
||
<p>A negation of a match pattern using the operator <code class="docutils literal notranslate"><span class="pre">!</span></code> as a prefix would match
|
||
exactly if the pattern itself does not match. For instance, <code class="docutils literal notranslate"><span class="pre">!(3</span> <span class="pre">|</span> <span class="pre">4)</span></code>
|
||
would match anything except <code class="docutils literal notranslate"><span class="pre">3</span></code> or <code class="docutils literal notranslate"><span class="pre">4</span></code>.</p>
|
||
<p>This was rejected because there is <a class="reference external" href="https://dl.acm.org/doi/abs/10.1145/2480360.2384582">documented evidence</a> that this feature
|
||
is rarely useful (in languages which support it) or used as double negation
|
||
<code class="docutils literal notranslate"><span class="pre">!!</span></code> to control variable scopes and prevent variable bindings (which does
|
||
not apply to Python). It can also be simulated using guard conditions.</p>
|
||
</section>
|
||
<section id="check-exhaustiveness-at-runtime">
|
||
<h3><a class="toc-backref" href="#check-exhaustiveness-at-runtime" role="doc-backlink">Check exhaustiveness at runtime</a></h3>
|
||
<p>The question is what to do if no case clause has a matching pattern, and
|
||
there is no default case. An earlier version of the proposal specified that
|
||
the behavior in this case would be to throw an exception rather than
|
||
silently falling through.</p>
|
||
<p>The arguments back and forth were many, but in the end the EIBTI (Explicit
|
||
Is Better Than Implicit) argument won out: it’s better to have the programmer
|
||
explicitly throw an exception if that is the behavior they want.</p>
|
||
<p>For cases such as sealed classes and enums, where the patterns are all known
|
||
to be members of a discrete set, <a class="reference internal" href="#static-checkers-specification">static checkers</a> can warn about missing
|
||
patterns.</p>
|
||
</section>
|
||
<section id="type-annotations-for-pattern-variables">
|
||
<h3><a class="toc-backref" href="#type-annotations-for-pattern-variables" role="doc-backlink">Type annotations for pattern variables</a></h3>
|
||
<p>The proposal was to combine patterns with type annotations:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">x</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">str</span><span class="p">]:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"An int </span><span class="si">{</span><span class="n">a</span><span class="si">}</span><span class="s2"> and a string </span><span class="si">{</span><span class="n">b</span><span class="si">}</span><span class="s2">:)</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="nb">int</span><span class="p">]:</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Three ints"</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This idea has a lot of problems. For one, the colon can only
|
||
be used inside of brackets or parens, otherwise the syntax becomes
|
||
ambiguous. And because Python disallows <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> checks
|
||
on generic types, type annotations containing generics will not
|
||
work as expected.</p>
|
||
</section>
|
||
<section id="allow-rest-in-class-patterns">
|
||
<h3><a class="toc-backref" href="#allow-rest-in-class-patterns" role="doc-backlink">Allow <code class="docutils literal notranslate"><span class="pre">*rest</span></code> in class patterns</a></h3>
|
||
<p>It was proposed to allow <code class="docutils literal notranslate"><span class="pre">*rest</span></code> in a class pattern, giving a
|
||
variable to be bound to all positional arguments at once (similar to
|
||
its use in unpacking assignments). It would provide some symmetry
|
||
with sequence patterns. But it might be confused with a feature to
|
||
provide the <em>values</em> for all positional arguments at once. And there
|
||
seems to be no practical need for it, so it was scrapped. (It could
|
||
easily be added at a later stage if a need arises.)</p>
|
||
</section>
|
||
<section id="disallow-a-in-constant-value-patterns">
|
||
<h3><a class="toc-backref" href="#disallow-a-in-constant-value-patterns" role="doc-backlink">Disallow <code class="docutils literal notranslate"><span class="pre">_.a</span></code> in constant value patterns</a></h3>
|
||
<p>The first public draft said that the initial name in a constant value
|
||
pattern must not be <code class="docutils literal notranslate"><span class="pre">_</span></code> because <code class="docutils literal notranslate"><span class="pre">_</span></code> has a special meaning in
|
||
pattern matching, so this would be invalid:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span><span class="w"> </span><span class="k">_</span><span class="o">.</span><span class="n">a</span><span class="p">:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>(However, <code class="docutils literal notranslate"><span class="pre">a._</span></code> would be legal and load the attribute with name
|
||
<code class="docutils literal notranslate"><span class="pre">_</span></code> of the object <code class="docutils literal notranslate"><span class="pre">a</span></code> as usual.)</p>
|
||
<p>There was some pushback against this on python-dev (some people have a
|
||
legitimate use for <code class="docutils literal notranslate"><span class="pre">_</span></code> as an important global variable, esp. in
|
||
i18n) and the only reason for this prohibition was to prevent some
|
||
user confusion. But it’s not the hill to die on.</p>
|
||
</section>
|
||
<section id="use-some-other-token-as-wildcard">
|
||
<h3><a class="toc-backref" href="#use-some-other-token-as-wildcard" role="doc-backlink">Use some other token as wildcard</a></h3>
|
||
<p>It has been proposed to use <code class="docutils literal notranslate"><span class="pre">...</span></code> (i.e., the ellipsis token) or
|
||
<code class="docutils literal notranslate"><span class="pre">*</span></code> (star) as a wildcard. However, both these look as if an
|
||
arbitrary number of items is omitted:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="o">...</span><span class="p">,</span> <span class="n">z</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">case</span> <span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">z</span><span class="p">]:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Both look like the would match a sequence of at two or more items,
|
||
capturing the first and last values.</p>
|
||
<p>In addition, if <code class="docutils literal notranslate"><span class="pre">*</span></code> were to be used as the wildcard character, we
|
||
would have to come up with some other way to capture the rest of a
|
||
sequence, currently spelled like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="p">[</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="p">]:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Using an ellipsis would also be more confusing in documentation and
|
||
examples, where <code class="docutils literal notranslate"><span class="pre">...</span></code> is routinely used to indicate something
|
||
obvious or irrelevant. (Yes, this would also be an argument against
|
||
the other uses of <code class="docutils literal notranslate"><span class="pre">...</span></code> in Python, but that water is already under
|
||
the bridge.)</p>
|
||
<p>Another proposal was to use <code class="docutils literal notranslate"><span class="pre">?</span></code>. This could be acceptable, although
|
||
it would require modifying the tokenizer.</p>
|
||
<p>Also, <code class="docutils literal notranslate"><span class="pre">_</span></code> is already used
|
||
as a throwaway target in other contexts, and this use is pretty
|
||
similar. This example is from <code class="docutils literal notranslate"><span class="pre">difflib.py</span></code> in the stdlib:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">tag</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">j1</span><span class="p">,</span> <span class="n">j2</span> <span class="ow">in</span> <span class="n">group</span><span class="p">:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Perhaps the most convincing argument is that <code class="docutils literal notranslate"><span class="pre">_</span></code> is used as the
|
||
wildcard in every other language we’ve looked at supporting pattern
|
||
matching: C#, Elixir, Erlang, F#, Haskell, Mathematica, OCaml, Ruby,
|
||
Rust, Scala, and Swift. Now, in general, we should not be concerned
|
||
too much with what another language does, since Python is clearly
|
||
different from all these languages. However, if there is such an
|
||
overwhelming and strong consensus, Python should not go out of its way
|
||
to do something completely different – particularly given that <code class="docutils literal notranslate"><span class="pre">_</span></code>
|
||
works well in Python and is already in use as a throwaway target.</p>
|
||
<p>Note that <code class="docutils literal notranslate"><span class="pre">_</span></code> is not assigned to by patterns – this avoids
|
||
conflicts with the use of <code class="docutils literal notranslate"><span class="pre">_</span></code> as a marker for translatable strings
|
||
and an alias for <code class="docutils literal notranslate"><span class="pre">gettext.gettext</span></code>, as recommended by the
|
||
<code class="docutils literal notranslate"><span class="pre">gettext</span></code> module documentation.</p>
|
||
</section>
|
||
<section id="use-some-other-syntax-instead-of-for-or-patterns">
|
||
<h3><a class="toc-backref" href="#use-some-other-syntax-instead-of-for-or-patterns" role="doc-backlink">Use some other syntax instead of <code class="docutils literal notranslate"><span class="pre">|</span></code> for OR patterns</a></h3>
|
||
<p>A few alternatives to using <code class="docutils literal notranslate"><span class="pre">|</span></code> to separate the alternatives in OR
|
||
patterns have been proposed. Instead of:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="mi">401</span><span class="o">|</span><span class="mi">403</span><span class="o">|</span><span class="mi">404</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Some HTTP error"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>the following proposals have been fielded:</p>
|
||
<ul>
|
||
<li>Use a comma:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="mi">401</span><span class="p">,</span> <span class="mi">403</span><span class="p">,</span> <span class="mi">404</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Some HTTP error"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This looks too much like a tuple – we would have to find a
|
||
different way to spell tuples, and the construct would have to be
|
||
parenthesized inside the argument list of a class pattern. In
|
||
general, commas already have many different meanings in Python, we
|
||
shouldn’t add more.</p>
|
||
</li>
|
||
<li>Allow stacked cases:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="mi">401</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="mi">403</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="mi">404</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Some HTTP error"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This is how this would be done in C, using its fall-through
|
||
semantics for cases. However, we don’t want to mislead people into
|
||
thinking that <code class="docutils literal notranslate"><span class="pre">match</span></code>/<code class="docutils literal notranslate"><span class="pre">case</span></code> uses fall-through semantics (which
|
||
are a common source of bugs in C). Also, this would be a novel
|
||
indentation pattern, which might make it harder to support in IDEs
|
||
and such (it would break the simple rule “add an indentation level
|
||
after a line ending in a colon”). Finally, this wouldn’t support
|
||
OR patterns nested inside other patterns.</p>
|
||
</li>
|
||
<li>Use <code class="docutils literal notranslate"><span class="pre">case</span> <span class="pre">in</span></code> followed by a comma-separated list:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">case</span> <span class="ow">in</span> <span class="mi">401</span><span class="p">,</span> <span class="mi">403</span><span class="p">,</span> <span class="mi">404</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Some HTTP error"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This wouldn’t work for OR patterns nested inside other patterns,
|
||
like:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">Point</span><span class="p">(</span><span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="o">|</span><span class="mi">1</span><span class="p">):</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"A corner of the unit square"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Use the <code class="docutils literal notranslate"><span class="pre">or</span></code> keyword:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="mi">401</span> <span class="ow">or</span> <span class="mi">403</span> <span class="ow">or</span> <span class="mi">404</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Some HTTP error"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This could work, and the readability is not too different from using
|
||
<code class="docutils literal notranslate"><span class="pre">|</span></code>. Some users expressed a preference for <code class="docutils literal notranslate"><span class="pre">or</span></code> because they
|
||
associate <code class="docutils literal notranslate"><span class="pre">|</span></code> with bitwise OR. However:</p>
|
||
<ol class="arabic">
|
||
<li>Many other languages that have pattern matching use <code class="docutils literal notranslate"><span class="pre">|</span></code> (the
|
||
list includes Elixir, Erlang, F#, Mathematica, OCaml, Ruby, Rust,
|
||
and Scala).</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">|</span></code> is shorter, which may contribute to the readability of
|
||
nested patterns like <code class="docutils literal notranslate"><span class="pre">Point(0|1,</span> <span class="pre">0|1)</span></code>.</li>
|
||
<li>Some people mistakenly believe that <code class="docutils literal notranslate"><span class="pre">|</span></code> has the wrong priority;
|
||
but since patterns don’t support other operators it has the same
|
||
priority as in expressions.</li>
|
||
<li>Python users use <code class="docutils literal notranslate"><span class="pre">or</span></code> very frequently, and may build an
|
||
impression that it is strongly associated with Boolean
|
||
short-circuiting.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">|</span></code> is used between alternatives in regular expressions
|
||
and in EBNF grammars (like Python’s own).</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">|</span></code> not just used for bitwise OR – it’s used for set unions,
|
||
dict merging (<a class="pep reference internal" href="../pep-0584/" title="PEP 584 – Add Union Operators To dict">PEP 584</a>) and is being considered as an
|
||
alternative to <code class="docutils literal notranslate"><span class="pre">typing.Union</span></code> (<a class="pep reference internal" href="../pep-0604/" title="PEP 604 – Allow writing union types as X | Y">PEP 604</a>).</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">|</span></code> works better as a visual separator, especially between
|
||
strings. Compare:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="s2">"spam"</span> <span class="ow">or</span> <span class="s2">"eggs"</span> <span class="ow">or</span> <span class="s2">"cheese"</span><span class="p">:</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="s2">"spam"</span> <span class="o">|</span> <span class="s2">"eggs"</span> <span class="o">|</span> <span class="s2">"cheese"</span><span class="p">:</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ol>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section id="add-an-else-clause">
|
||
<h3><a class="toc-backref" href="#add-an-else-clause" role="doc-backlink">Add an <code class="docutils literal notranslate"><span class="pre">else</span></code> clause</a></h3>
|
||
<p>We decided not to add an <code class="docutils literal notranslate"><span class="pre">else</span></code> clause for several reasons.</p>
|
||
<ul class="simple">
|
||
<li>It is redundant, since we already have <code class="docutils literal notranslate"><span class="pre">case</span> <span class="pre">_:</span></code></li>
|
||
<li>There will forever be confusion about the indentation level of the
|
||
<code class="docutils literal notranslate"><span class="pre">else:</span></code> – should it align with the list of cases or with the
|
||
<code class="docutils literal notranslate"><span class="pre">match</span></code> keyword?</li>
|
||
<li>Completionist arguments like “every other statement has one” are
|
||
false – only those statements have an <code class="docutils literal notranslate"><span class="pre">else</span></code> clause where it adds
|
||
new functionality.</li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
<section id="deferred-ideas">
|
||
<h2><a class="toc-backref" href="#deferred-ideas" role="doc-backlink">Deferred Ideas</a></h2>
|
||
<p>There were a number of proposals to extend the matching syntax that we
|
||
decided to postpone for possible future PEP. These fall into the realm of
|
||
“cool idea but not essential”, and it was felt that it might be better to
|
||
acquire some real-world data on how the match statement will be used in
|
||
practice before moving forward with some of these proposals.</p>
|
||
<p>Note that in each case, the idea was judged to be a “two-way door”,
|
||
meaning that there should be no backwards-compatibility issues with adding
|
||
these features later.</p>
|
||
<section id="one-off-syntax-variant">
|
||
<h3><a class="toc-backref" href="#one-off-syntax-variant" role="doc-backlink">One-off syntax variant</a></h3>
|
||
<p>While inspecting some code-bases that may benefit the most from the proposed
|
||
syntax, it was found that single clause matches would be used relatively often,
|
||
mostly for various special-casing. In other languages this is supported in
|
||
the form of one-off matches. We proposed to support such one-off matches too:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">match</span> <span class="n">value</span> <span class="k">as</span> <span class="n">pattern</span> <span class="p">[</span><span class="ow">and</span> <span class="n">guard</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>or, alternatively, without the <code class="docutils literal notranslate"><span class="pre">if</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">value</span> <span class="k">as</span> <span class="n">pattern</span> <span class="p">[</span><span class="k">if</span> <span class="n">guard</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>as equivalent to the following expansion:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">pattern</span> <span class="p">[</span><span class="k">if</span> <span class="n">guard</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To illustrate how this will benefit readability, consider this (slightly
|
||
simplified) snippet from real code:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">CallExpr</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">callee</span><span class="p">,</span> <span class="n">NameExpr</span><span class="p">)</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span>
|
||
<span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">NameExpr</span><span class="p">)):</span>
|
||
<span class="n">call</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="n">callee</span><span class="o">.</span><span class="n">name</span>
|
||
<span class="n">arg</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">name</span>
|
||
<span class="o">...</span> <span class="c1"># Continue special-casing 'call' and 'arg'</span>
|
||
<span class="o">...</span> <span class="c1"># Follow with common code</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This can be rewritten in a more straightforward way as:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">match</span> <span class="n">node</span> <span class="k">as</span> <span class="n">CallExpr</span><span class="p">(</span><span class="n">callee</span><span class="o">=</span><span class="n">NameExpr</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">call</span><span class="p">),</span> <span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="n">NameExpr</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">arg</span><span class="p">)]):</span>
|
||
<span class="o">...</span> <span class="c1"># Continue special-casing 'call' and 'arg'</span>
|
||
<span class="o">...</span> <span class="c1"># Follow with common code</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This one-off form would not allow <code class="docutils literal notranslate"><span class="pre">elif</span> <span class="pre">match</span></code> statements, as it was only
|
||
meant to handle a single pattern case. It was intended to be special case
|
||
of a <code class="docutils literal notranslate"><span class="pre">match</span></code> statement, not a special case of an <code class="docutils literal notranslate"><span class="pre">if</span></code> statement:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">match</span> <span class="n">value_1</span> <span class="k">as</span> <span class="n">patter_1</span> <span class="p">[</span><span class="ow">and</span> <span class="n">guard_1</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">elif</span> <span class="n">match</span> <span class="n">value_2</span> <span class="k">as</span> <span class="n">pattern_2</span> <span class="p">[</span><span class="ow">and</span> <span class="n">guard_2</span><span class="p">]:</span> <span class="c1"># Not allowed</span>
|
||
<span class="o">...</span>
|
||
<span class="k">elif</span> <span class="n">match</span> <span class="n">value_3</span> <span class="k">as</span> <span class="n">pattern_3</span> <span class="p">[</span><span class="ow">and</span> <span class="n">guard_3</span><span class="p">]:</span> <span class="c1"># Not allowed</span>
|
||
<span class="o">...</span>
|
||
<span class="k">else</span><span class="p">:</span> <span class="c1"># Also not allowed</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This would defeat the purpose of one-off matches as a complement to exhaustive
|
||
full matches - it’s better and clearer to use a full match in this case.</p>
|
||
<p>Similarly, <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">not</span> <span class="pre">match</span></code> would not be allowed, since <code class="docutils literal notranslate"><span class="pre">match</span> <span class="pre">...</span> <span class="pre">as</span> <span class="pre">...</span></code> is not
|
||
an expression. Nor do we propose a <code class="docutils literal notranslate"><span class="pre">while</span> <span class="pre">match</span></code> construct present in some languages
|
||
with pattern matching, since although it may be handy, it will likely be used
|
||
rarely.</p>
|
||
</section>
|
||
<section id="other-pattern-based-constructions">
|
||
<h3><a class="toc-backref" href="#other-pattern-based-constructions" role="doc-backlink">Other pattern-based constructions</a></h3>
|
||
<p>Many other languages supporting pattern-matching use it as a basis for multiple
|
||
language constructs, including a matching operator, a generalized form
|
||
of assignment, a filter for loops, a method for synchronizing communication,
|
||
or specialized if statements. Some of these were mentioned in the discussion
|
||
of the first draft. Another question asked was why this particular form (joining
|
||
binding and conditional selection) was chosen while other forms were not.</p>
|
||
<p>Introducing more uses of patterns would be too bold and premature given the
|
||
experience we have using patterns, and would make this proposal too
|
||
complicated. The statement as presented provides a form of the feature that
|
||
is sufficiently general to be useful while being self-contained, and without
|
||
having a massive impact on the syntax and semantics of the language as a whole.</p>
|
||
<p>After some experience with this feature, the community may have a better
|
||
feeling for what other uses of pattern matching could be valuable in Python.</p>
|
||
</section>
|
||
<section id="algebraic-matching-of-repeated-names">
|
||
<h3><a class="toc-backref" href="#algebraic-matching-of-repeated-names" role="doc-backlink">Algebraic matching of repeated names</a></h3>
|
||
<p>A technique occasionally seen in functional languages like Erlang and Elixir is
|
||
to use a match variable multiple times in the same pattern:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Point is on a diagonal!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The idea here is that the first appearance of <code class="docutils literal notranslate"><span class="pre">x</span></code> would bind the value
|
||
to the name, and subsequent occurrences would verify that the incoming
|
||
value was equal to the value previously bound. If the value was not equal,
|
||
the match would fail.</p>
|
||
<p>However, there are a number of subtleties involved with mixing load-store
|
||
semantics for capture patterns. For the moment, we decided to make repeated
|
||
use of names within the same pattern an error; we can always relax this
|
||
restriction later without affecting backwards compatibility.</p>
|
||
<p>Note that you <strong>can</strong> use the same name more than once in alternate choices:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">value</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">x</span> <span class="o">|</span> <span class="p">[</span><span class="n">x</span><span class="p">]:</span>
|
||
<span class="c1"># etc.</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="custom-matching-protocol">
|
||
<span id="extended-matching"></span><h3><a class="toc-backref" href="#custom-matching-protocol" role="doc-backlink">Custom matching protocol</a></h3>
|
||
<p>During the initial design discussions for this PEP, there were a lot of ideas
|
||
thrown around about custom matchers. There were a couple of motivations for
|
||
this:</p>
|
||
<ul class="simple">
|
||
<li>Some classes might want to expose a different set of “matchable” names
|
||
than the actual class properties.</li>
|
||
<li>Some classes might have properties that are expensive to calculate, and
|
||
therefore shouldn’t be evaluated unless the match pattern actually needed
|
||
access to them.</li>
|
||
<li>There were ideas for exotic matchers such as <code class="docutils literal notranslate"><span class="pre">IsInstance()</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">InRange()</span></code>, <code class="docutils literal notranslate"><span class="pre">RegexMatchingGroup()</span></code> and so on.</li>
|
||
<li>In order for built-in types and standard library classes to be able
|
||
to support matching in a reasonable and intuitive way, it was believed
|
||
that these types would need to implement special matching logic.</li>
|
||
</ul>
|
||
<p>These customized match behaviors would be controlled by a special
|
||
<code class="docutils literal notranslate"><span class="pre">__match__</span></code> method on the class name. There were two competing variants:</p>
|
||
<ul class="simple">
|
||
<li>A ‘full-featured’ match protocol which would pass in not only
|
||
the subject to be matched, but detailed information about
|
||
which attributes the specified pattern was interested in.</li>
|
||
<li>A simplified match protocol, which only passed in the subject value,
|
||
and which returned a “proxy object” (which in most cases could be
|
||
just the subject) containing the matchable attributes.</li>
|
||
</ul>
|
||
<p>Here’s an example of one version of the more complex protocol proposed:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">expr</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">BinaryOp</span><span class="p">(</span><span class="n">left</span><span class="o">=</span><span class="n">Number</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="n">x</span><span class="p">),</span> <span class="n">op</span><span class="o">=</span><span class="n">op</span><span class="p">,</span> <span class="n">right</span><span class="o">=</span><span class="n">Number</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="n">y</span><span class="p">)):</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">types</span> <span class="kn">import</span> <span class="n">PatternObject</span>
|
||
<span class="n">BinaryOp</span><span class="o">.</span><span class="n">__match__</span><span class="p">(</span>
|
||
<span class="p">(),</span>
|
||
<span class="p">{</span>
|
||
<span class="s2">"left"</span><span class="p">:</span> <span class="n">PatternObject</span><span class="p">(</span><span class="n">Number</span><span class="p">,</span> <span class="p">(),</span> <span class="p">{</span><span class="s2">"value"</span><span class="p">:</span> <span class="o">...</span><span class="p">},</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
|
||
<span class="s2">"op"</span><span class="p">:</span> <span class="o">...</span><span class="p">,</span>
|
||
<span class="s2">"right"</span><span class="p">:</span> <span class="n">PatternObject</span><span class="p">(</span><span class="n">Number</span><span class="p">,</span> <span class="p">(),</span> <span class="p">{</span><span class="s2">"value"</span><span class="p">:</span> <span class="o">...</span><span class="p">},</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="kc">False</span><span class="p">),</span>
|
||
<span class="p">},</span>
|
||
<span class="o">-</span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="kc">False</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>One drawback of this protocol is that the arguments to <code class="docutils literal notranslate"><span class="pre">__match__</span></code>
|
||
would be expensive to construct, and could not be pre-computed due to
|
||
the fact that, because of the way names are bound, there are no real
|
||
constants in Python. It also meant that the <code class="docutils literal notranslate"><span class="pre">__match__</span></code> method would
|
||
have to re-implement much of the logic of matching which would otherwise
|
||
be implemented in C code in the Python VM. As a result, this option would
|
||
perform poorly compared to an equivalent <code class="docutils literal notranslate"><span class="pre">if</span></code>-statement.</p>
|
||
<p>The simpler protocol suffered from the fact that although it was more
|
||
performant, it was much less flexible, and did not allow for many of
|
||
the creative custom matchers that people were dreaming up.</p>
|
||
<p>Late in the design process, however, it was realized that the need for
|
||
a custom matching protocol was much less than anticipated. Virtually
|
||
all the realistic (as opposed to fanciful) uses cases brought up could
|
||
be handled by the built-in matching behavior, although in a few cases
|
||
an extra guard condition was required to get the desired effect.</p>
|
||
<p>Moreover, it turned out that none of the standard library classes really
|
||
needed any special matching support other than an appropriate
|
||
<code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> property.</p>
|
||
<p>The decision to postpone this feature came with a realization that this is
|
||
not a one-way door; that a more flexible and customizable matching protocol
|
||
can be added later, especially as we gain more experience with real-world
|
||
use cases and actual user needs.</p>
|
||
<p>The authors of this PEP expect that the <code class="docutils literal notranslate"><span class="pre">match</span></code> statement will evolve
|
||
over time as usage patterns and idioms evolve, in a way similar to what
|
||
other “multi-stage” PEPs have done in the past. When this happens, the
|
||
extended matching issue can be revisited.</p>
|
||
</section>
|
||
<section id="parameterized-matching-syntax">
|
||
<h3><a class="toc-backref" href="#parameterized-matching-syntax" role="doc-backlink">Parameterized Matching Syntax</a></h3>
|
||
<p>(Also known as “Class Instance Matchers”.)</p>
|
||
<p>This is another variant of the “custom match classes” idea that would allow
|
||
diverse kinds of custom matchers mentioned in the previous section – however,
|
||
instead of using an extended matching protocol, it would be achieved by
|
||
introducing an additional pattern type with its own syntax. This pattern type
|
||
would accept two distinct sets of parameters: one set which consists of the
|
||
actual parameters passed into the pattern object’s constructor, and another
|
||
set representing the binding variables for the pattern.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">__match__</span></code> method of these objects could use the constructor parameter
|
||
values in deciding what was a valid match.</p>
|
||
<p>This would allow patterns such as <code class="docutils literal notranslate"><span class="pre">InRange<0,</span> <span class="pre">6>(value)</span></code>, which would match
|
||
a number in the range 0..6 and assign the matched value to ‘value’. Similarly,
|
||
one could have a pattern which tests for the existence of a named group in
|
||
a regular expression match result (different meaning of the word ‘match’).</p>
|
||
<p>Although there is some support for this idea, there was a lot of bikeshedding
|
||
on the syntax (there are not a lot of attractive options available)
|
||
and no clear consensus was reached, so it was decided that for now, this
|
||
feature is not essential to the PEP.</p>
|
||
</section>
|
||
<section id="pattern-utility-library">
|
||
<h3><a class="toc-backref" href="#pattern-utility-library" role="doc-backlink">Pattern Utility Library</a></h3>
|
||
<p>Both of the previous ideas would be accompanied by a new Python standard
|
||
library module which would contain a rich set of useful matchers.
|
||
However, it is not really possible to implement such a library without
|
||
adopting one of the extended pattern proposals given in the previous sections,
|
||
so this idea is also deferred.</p>
|
||
</section>
|
||
</section>
|
||
<section id="acknowledgments">
|
||
<h2><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h2>
|
||
<p>We are grateful for the help of the following individuals (among many
|
||
others) for helping out during various phases of the writing of this
|
||
PEP:</p>
|
||
<ul class="simple">
|
||
<li>Gregory P. Smith</li>
|
||
<li>Jim Jewett</li>
|
||
<li>Mark Shannon</li>
|
||
<li>Nate Lust</li>
|
||
<li>Taine Zhao</li>
|
||
</ul>
|
||
</section>
|
||
<section id="version-history">
|
||
<h2><a class="toc-backref" href="#version-history" role="doc-backlink">Version History</a></h2>
|
||
<ol class="arabic simple">
|
||
<li>Initial version</li>
|
||
<li>Substantial rewrite, including:<ul class="simple">
|
||
<li>Minor clarifications, grammar and typo corrections</li>
|
||
<li>Rename various concepts</li>
|
||
<li>Additional discussion of rejected ideas, including:<ul>
|
||
<li>Why we choose <code class="docutils literal notranslate"><span class="pre">_</span></code> for wildcard patterns</li>
|
||
<li>Why we choose <code class="docutils literal notranslate"><span class="pre">|</span></code> for OR patterns</li>
|
||
<li>Why we choose not to use special syntax for capture variables</li>
|
||
<li>Why this pattern matching operation and not others</li>
|
||
</ul>
|
||
</li>
|
||
<li>Clarify exception and side effect semantics</li>
|
||
<li>Clarify partial binding semantics</li>
|
||
<li>Drop restriction on use of <code class="docutils literal notranslate"><span class="pre">_</span></code> in load contexts</li>
|
||
<li>Drop the default single positional argument being the whole
|
||
subject except for a handful of built-in types</li>
|
||
<li>Simplify behavior of <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code></li>
|
||
<li>Drop the <code class="docutils literal notranslate"><span class="pre">__match__</span></code> protocol (moved to <a class="reference internal" href="#deferred-ideas">deferred ideas</a>)</li>
|
||
<li>Drop <code class="docutils literal notranslate"><span class="pre">ImpossibleMatchError</span></code> exception</li>
|
||
<li>Drop leading dot for loads (moved to <a class="reference internal" href="#deferred-ideas">deferred ideas</a>)</li>
|
||
<li>Reworked the initial sections (everything before <a class="reference internal" href="#syntax-and-semantics">syntax</a>)</li>
|
||
<li>Added an overview of all the types of patterns before the
|
||
detailed description</li>
|
||
<li>Added simplified syntax next to the description of each pattern</li>
|
||
<li>Separate description of the wildcard from capture patterns</li>
|
||
<li>Added Daniel F Moisset as sixth co-author</li>
|
||
</ul>
|
||
</li>
|
||
</ol>
|
||
</section>
|
||
<section id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id3" role="doc-footnote">
|
||
<dt class="label" id="id3">[<a href="#id2">1</a>]</dt>
|
||
<dd><a class="reference external" href="https://github.com/gvanrossum/patma/blob/master/EXAMPLES.md">https://github.com/gvanrossum/patma/blob/master/EXAMPLES.md</a></aside>
|
||
</aside>
|
||
</section>
|
||
<section id="appendix-a-full-grammar">
|
||
<h2><a class="toc-backref" href="#appendix-a-full-grammar" role="doc-backlink">Appendix A – Full Grammar</a></h2>
|
||
<p>Here is the full grammar for <code class="docutils literal notranslate"><span class="pre">match_stmt</span></code>. This is an additional
|
||
alternative for <code class="docutils literal notranslate"><span class="pre">compound_stmt</span></code>. It should be understood that
|
||
<code class="docutils literal notranslate"><span class="pre">match</span></code> and <code class="docutils literal notranslate"><span class="pre">case</span></code> are soft keywords, i.e. they are not reserved
|
||
words in other grammatical contexts (including at the start of a line
|
||
if there is no colon where expected). By convention, hard keywords
|
||
use single quotes while soft keywords use double quotes.</p>
|
||
<p>Other notation used beyond standard EBNF:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">SEP.RULE+</span></code> is shorthand for <code class="docutils literal notranslate"><span class="pre">RULE</span> <span class="pre">(SEP</span> <span class="pre">RULE)*</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">!RULE</span></code> is a negative lookahead assertion</li>
|
||
</ul>
|
||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>match_expr:
|
||
| star_named_expression ',' star_named_expressions?
|
||
| named_expression
|
||
match_stmt: "match" match_expr ':' NEWLINE INDENT case_block+ DEDENT
|
||
case_block: "case" patterns [guard] ':' block
|
||
guard: 'if' named_expression
|
||
patterns: value_pattern ',' [values_pattern] | pattern
|
||
pattern: walrus_pattern | or_pattern
|
||
walrus_pattern: NAME ':=' or_pattern
|
||
or_pattern: '|'.closed_pattern+
|
||
closed_pattern:
|
||
| capture_pattern
|
||
| literal_pattern
|
||
| constant_pattern
|
||
| group_pattern
|
||
| sequence_pattern
|
||
| mapping_pattern
|
||
| class_pattern
|
||
capture_pattern: NAME !('.' | '(' | '=')
|
||
literal_pattern:
|
||
| signed_number !('+' | '-')
|
||
| signed_number '+' NUMBER
|
||
| signed_number '-' NUMBER
|
||
| strings
|
||
| 'None'
|
||
| 'True'
|
||
| 'False'
|
||
constant_pattern: attr !('.' | '(' | '=')
|
||
group_pattern: '(' patterns ')'
|
||
sequence_pattern: '[' [values_pattern] ']' | '(' ')'
|
||
mapping_pattern: '{' items_pattern? '}'
|
||
class_pattern:
|
||
| name_or_attr '(' ')'
|
||
| name_or_attr '(' ','.pattern+ ','? ')'
|
||
| name_or_attr '(' ','.keyword_pattern+ ','? ')'
|
||
| name_or_attr '(' ','.pattern+ ',' ','.keyword_pattern+ ','? ')'
|
||
signed_number: NUMBER | '-' NUMBER
|
||
attr: name_or_attr '.' NAME
|
||
name_or_attr: attr | NAME
|
||
values_pattern: ','.value_pattern+ ','?
|
||
items_pattern: ','.key_value_pattern+ ','?
|
||
keyword_pattern: NAME '=' or_pattern
|
||
value_pattern: '*' capture_pattern | pattern
|
||
key_value_pattern:
|
||
| (literal_pattern | constant_pattern) ':' or_pattern
|
||
| '**' capture_pattern
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document is placed in the public domain or under the
|
||
CC0-1.0-Universal license, whichever is more permissive.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0622.rst">https://github.com/python/peps/blob/main/peps/pep-0622.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0622.rst">2023-09-09 17:39:29 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a><ul>
|
||
<li><a class="reference internal" href="#patterns-and-shapes">Patterns and shapes</a></li>
|
||
<li><a class="reference internal" href="#syntax">Syntax</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#overview">Overview</a><ul>
|
||
<li><a class="reference internal" href="#pattern-a-new-syntactic-construct-and-destructuring">Pattern, a new syntactic construct, and destructuring</a></li>
|
||
<li><a class="reference internal" href="#matching-process">Matching process</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rationale-and-goals">Rationale and Goals</a></li>
|
||
<li><a class="reference internal" href="#syntax-and-semantics">Syntax and Semantics</a><ul>
|
||
<li><a class="reference internal" href="#patterns">Patterns</a></li>
|
||
<li><a class="reference internal" href="#the-match-statement">The <code class="docutils literal notranslate"><span class="pre">match</span></code> statement</a></li>
|
||
<li><a class="reference internal" href="#match-semantics">Match semantics</a></li>
|
||
<li><a class="reference internal" href="#allowed-patterns">Allowed patterns</a><ul>
|
||
<li><a class="reference internal" href="#literal-patterns">Literal Patterns</a></li>
|
||
<li><a class="reference internal" href="#capture-patterns">Capture Patterns</a></li>
|
||
<li><a class="reference internal" href="#wildcard-pattern">Wildcard Pattern</a></li>
|
||
<li><a class="reference internal" href="#constant-value-patterns">Constant Value Patterns</a></li>
|
||
<li><a class="reference internal" href="#sequence-patterns">Sequence Patterns</a></li>
|
||
<li><a class="reference internal" href="#mapping-patterns">Mapping Patterns</a></li>
|
||
<li><a class="reference internal" href="#class-patterns">Class Patterns</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#combining-multiple-patterns-or-patterns">Combining multiple patterns (OR patterns)</a></li>
|
||
<li><a class="reference internal" href="#guards">Guards</a></li>
|
||
<li><a class="reference internal" href="#walrus-patterns">Walrus patterns</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#runtime-specification">Runtime specification</a><ul>
|
||
<li><a class="reference internal" href="#the-match-protocol">The Match Protocol</a></li>
|
||
<li><a class="reference internal" href="#overlapping-sub-patterns">Overlapping sub-patterns</a></li>
|
||
<li><a class="reference internal" href="#special-attribute-match-args">Special attribute <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code></a></li>
|
||
<li><a class="reference internal" href="#exceptions-and-side-effects">Exceptions and side effects</a></li>
|
||
<li><a class="reference internal" href="#the-standard-library">The standard library</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#static-checkers-specification">Static checkers specification</a><ul>
|
||
<li><a class="reference internal" href="#exhaustiveness-checks">Exhaustiveness checks</a></li>
|
||
<li><a class="reference internal" href="#sealed-classes-as-algebraic-data-types">Sealed classes as algebraic data types</a></li>
|
||
<li><a class="reference internal" href="#type-erasure">Type erasure</a></li>
|
||
<li><a class="reference internal" href="#note-about-constants">Note about constants</a></li>
|
||
<li><a class="reference internal" href="#precise-type-checking-of-star-matches">Precise type checking of star matches</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#performance-considerations">Performance Considerations</a></li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
|
||
<li><a class="reference internal" href="#impacts-on-third-party-tools">Impacts on third-party tools</a></li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#example-code">Example Code</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#don-t-do-this-pattern-matching-is-hard-to-learn">Don’t do this, pattern matching is hard to learn</a></li>
|
||
<li><a class="reference internal" href="#don-t-do-this-use-existing-method-dispatching-mechanisms">Don’t do this, use existing method dispatching mechanisms</a></li>
|
||
<li><a class="reference internal" href="#allow-more-flexible-assignment-targets-instead">Allow more flexible assignment targets instead</a></li>
|
||
<li><a class="reference internal" href="#make-it-an-expression">Make it an expression</a></li>
|
||
<li><a class="reference internal" href="#use-a-hard-keyword">Use a hard keyword</a></li>
|
||
<li><a class="reference internal" href="#use-as-or-instead-of-case-for-case-clauses">Use <code class="docutils literal notranslate"><span class="pre">as</span></code> or <code class="docutils literal notranslate"><span class="pre">|</span></code> instead of <code class="docutils literal notranslate"><span class="pre">case</span></code> for case clauses</a></li>
|
||
<li><a class="reference internal" href="#use-a-flat-indentation-scheme">Use a flat indentation scheme</a></li>
|
||
<li><a class="reference internal" href="#alternatives-for-constant-value-pattern">Alternatives for constant value pattern</a></li>
|
||
<li><a class="reference internal" href="#disallow-float-literals-in-patterns">Disallow float literals in patterns</a></li>
|
||
<li><a class="reference internal" href="#range-matching-patterns">Range matching patterns</a></li>
|
||
<li><a class="reference internal" href="#use-dispatch-dict-semantics-for-matches">Use dispatch dict semantics for matches</a></li>
|
||
<li><a class="reference internal" href="#use-continue-and-break-in-case-clauses">Use <code class="docutils literal notranslate"><span class="pre">continue</span></code> and <code class="docutils literal notranslate"><span class="pre">break</span></code> in case clauses.</a></li>
|
||
<li><a class="reference internal" href="#and-patterns">AND (<code class="docutils literal notranslate"><span class="pre">&</span></code>) patterns</a></li>
|
||
<li><a class="reference internal" href="#negative-match-patterns">Negative match patterns</a></li>
|
||
<li><a class="reference internal" href="#check-exhaustiveness-at-runtime">Check exhaustiveness at runtime</a></li>
|
||
<li><a class="reference internal" href="#type-annotations-for-pattern-variables">Type annotations for pattern variables</a></li>
|
||
<li><a class="reference internal" href="#allow-rest-in-class-patterns">Allow <code class="docutils literal notranslate"><span class="pre">*rest</span></code> in class patterns</a></li>
|
||
<li><a class="reference internal" href="#disallow-a-in-constant-value-patterns">Disallow <code class="docutils literal notranslate"><span class="pre">_.a</span></code> in constant value patterns</a></li>
|
||
<li><a class="reference internal" href="#use-some-other-token-as-wildcard">Use some other token as wildcard</a></li>
|
||
<li><a class="reference internal" href="#use-some-other-syntax-instead-of-for-or-patterns">Use some other syntax instead of <code class="docutils literal notranslate"><span class="pre">|</span></code> for OR patterns</a></li>
|
||
<li><a class="reference internal" href="#add-an-else-clause">Add an <code class="docutils literal notranslate"><span class="pre">else</span></code> clause</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
|
||
<li><a class="reference internal" href="#one-off-syntax-variant">One-off syntax variant</a></li>
|
||
<li><a class="reference internal" href="#other-pattern-based-constructions">Other pattern-based constructions</a></li>
|
||
<li><a class="reference internal" href="#algebraic-matching-of-repeated-names">Algebraic matching of repeated names</a></li>
|
||
<li><a class="reference internal" href="#custom-matching-protocol">Custom matching protocol</a></li>
|
||
<li><a class="reference internal" href="#parameterized-matching-syntax">Parameterized Matching Syntax</a></li>
|
||
<li><a class="reference internal" href="#pattern-utility-library">Pattern Utility Library</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
|
||
<li><a class="reference internal" href="#version-history">Version History</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#appendix-a-full-grammar">Appendix A – Full Grammar</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0622.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |