peps/pep-0653/index.html

1044 lines
71 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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 653 Precise Semantics for Pattern Matching | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0653/">
<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 653 Precise Semantics for Pattern Matching | peps.python.org'>
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0653/">
<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="Python Enhancement Proposals (PEPs)">
<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> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 653</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 653 Precise Semantics for Pattern Matching</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Mark Shannon &lt;mark&#32;&#97;t&#32;hotpy.org&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Proposal under active discussion and revision">Draft</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">09-Feb-2021</dd>
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
<dd class="field-odd">18-Feb-2021</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></li>
<li><a class="reference internal" href="#motivation">Motivation</a><ul>
<li><a class="reference internal" href="#precise-semantics">Precise semantics</a></li>
<li><a class="reference internal" href="#improved-control-over-class-matching">Improved control over class matching</a></li>
<li><a class="reference internal" href="#robustness">Robustness</a></li>
<li><a class="reference internal" href="#efficient-implementation">Efficient implementation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#additions-to-the-object-model">Additions to the object model</a></li>
<li><a class="reference internal" href="#semantics-of-the-matching-process">Semantics of the matching process</a><ul>
<li><a class="reference internal" href="#preamble">Preamble</a></li>
<li><a class="reference internal" href="#capture-patterns">Capture patterns</a></li>
<li><a class="reference internal" href="#wildcard-patterns">Wildcard patterns</a></li>
<li><a class="reference internal" href="#literal-patterns">Literal Patterns</a></li>
<li><a class="reference internal" href="#value-patterns">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>
<li><a class="reference internal" href="#nested-patterns">Nested patterns</a></li>
<li><a class="reference internal" href="#guards">Guards</a></li>
<li><a class="reference internal" href="#non-conforming-special-attributes">Non-conforming special attributes</a></li>
</ul>
</li>
<li><a class="reference internal" href="#values-of-the-special-attributes-for-classes-in-the-standard-library">Values of the special attributes for classes in the standard library</a></li>
<li><a class="reference internal" href="#legal-optimizations">Legal optimizations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#implementation">Implementation</a><ul>
<li><a class="reference internal" href="#possible-optimizations">Possible optimizations</a><ul>
<li><a class="reference internal" href="#splitting-evaluation-into-lanes">Splitting evaluation into lanes</a></li>
<li><a class="reference internal" href="#id9">Sequence patterns</a></li>
<li><a class="reference internal" href="#id10">Mapping patterns</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#summary-of-differences-between-this-pep-and-pep-634">Summary of differences between this PEP and PEP 634</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#using-attributes-from-the-instance-s-dictionary">Using attributes from the instances dictionary</a></li>
<li><a class="reference internal" href="#lookup-of-match-args-on-the-subject-not-the-pattern">Lookup of <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> on the subject not the pattern</a></li>
<li><a class="reference internal" href="#combining-match-class-and-match-container-into-a-single-value">Combining <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> and <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> into a single value</a></li>
</ul>
</li>
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
<li><a class="reference internal" href="#having-a-separate-value-to-reject-all-class-matches">Having a separate value to reject all class matches</a></li>
</ul>
</li>
<li><a class="reference internal" href="#code-examples">Code examples</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 a semantics for pattern matching that respects the general concept of <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a>,
but is more precise, easier to reason about, and should be faster.</p>
<p>The object model will be extended with two special (dunder) attributes, <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> and
<code class="docutils literal notranslate"><span class="pre">__match_class__</span></code>, in addition to the <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> attribute from <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a>, to support pattern matching.
Both of these new attributes must be integers and <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> is required to be a tuple of unique strings.</p>
<p>With this PEP:</p>
<ul class="simple">
<li>The semantics of pattern matching will be clearer, so that patterns are easier to reason about.</li>
<li>It will be possible to implement pattern matching in a more efficient fashion.</li>
<li>Pattern matching will be more usable for complex classes, by allowing classes some more control over which patterns they match.</li>
</ul>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>Pattern matching in Python, as described in <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a>, is to be added to Python 3.10.
Unfortunately, <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a> is not as precise about the semantics as it could be,
nor does it allow classes sufficient control over how they match patterns.</p>
<section id="precise-semantics">
<h3><a class="toc-backref" href="#precise-semantics" role="doc-backlink">Precise semantics</a></h3>
<p><a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a> explicitly includes a section on undefined behavior.
Large amounts of undefined behavior may be acceptable in a language like C,
but in Python it should be kept to a minimum.
Pattern matching in Python can be defined more precisely without losing expressiveness or performance.</p>
</section>
<section id="improved-control-over-class-matching">
<h3><a class="toc-backref" href="#improved-control-over-class-matching" role="doc-backlink">Improved control over class matching</a></h3>
<p><a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a> delegates the decision over whether a class is a sequence or mapping to <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code>.
Not all classes that could be considered sequences are registered as subclasses of <code class="docutils literal notranslate"><span class="pre">collections.abc.Sequence</span></code>.
This PEP allows them to match sequence patterns, without the full <code class="docutils literal notranslate"><span class="pre">collections.abc.Sequence</span></code> machinery.</p>
<p><a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a> privileges some builtin classes with a special form of matching, the “self” match.
For example the pattern <code class="docutils literal notranslate"><span class="pre">list(x)</span></code> matches a list and assigns the list to <code class="docutils literal notranslate"><span class="pre">x</span></code>.
By allowing classes to choose which kinds of pattern they match, other classes can use this form as well.</p>
<p>For example, using <code class="docutils literal notranslate"><span class="pre">sympy</span></code>, we might want to write:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># a*a == a**2</span>
<span class="k">case</span> <span class="n">Mul</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="n">Symbol</span><span class="p">(</span><span class="n">a</span><span class="p">),</span> <span class="n">Symbol</span><span class="p">(</span><span class="n">b</span><span class="p">)])</span> <span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">:</span>
<span class="k">return</span> <span class="n">Pow</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</pre></div>
</div>
<p>Which requires the sympy class <code class="docutils literal notranslate"><span class="pre">Symbol</span></code> to “self” match.
For <code class="docutils literal notranslate"><span class="pre">sympy</span></code> to support this pattern with <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a> is possible, but a bit tricky.
With this PEP it can be implemented very easily <a class="footnote-reference brackets" href="#id12" id="id1">[1]</a>.</p>
</section>
<section id="robustness">
<h3><a class="toc-backref" href="#robustness" role="doc-backlink">Robustness</a></h3>
<p>With this PEP, access to attributes during pattern matching becomes well defined and deterministic.
This makes pattern matching less error prone when matching objects with hidden side effects, such as object-relational mappers.
Objects will have more control over their own deconstruction, which can help prevent unintended consequences should attribute access have side-effects.</p>
<p><a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a> relies on the <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code> module when determining which patterns a value can match, implicitly importing it if necessary.
This PEP will eliminate surprising import errors and misleading audit events from those imports.</p>
</section>
<section id="efficient-implementation">
<h3><a class="toc-backref" href="#efficient-implementation" role="doc-backlink">Efficient implementation</a></h3>
<p>The semantics proposed in this PEP will allow efficient implementation, partly as a result of having precise semantics
and partly from using the object model.</p>
<p>With precise semantics, it is possible to reason about what code transformations are correct,
and thus apply optimizations effectively.</p>
<p>Because the object model is a core part of Python, implementations already handle special attribute lookup efficiently.
Looking up a special attribute is much faster than performing a subclass test on an abstract base class.</p>
</section>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>The object model and special methods are at the core of the Python language. Consequently,
implementations support them well.
Using special attributes for pattern matching allows pattern matching to be implemented in a way that
integrates well with the rest of the implementation, and is thus easier to maintain and is likely to perform better.</p>
<p>A match statement performs a sequence of pattern matches. In general, matching a pattern has three parts:</p>
<ol class="arabic simple">
<li>Can the value match this kind of pattern?</li>
<li>When deconstructed, does the value match this particular pattern?</li>
<li>Is the guard true?</li>
</ol>
<p>To determine whether a value can match a particular kind of pattern, we add the <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code>
and <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> attributes.
This allows the kind of a value to be determined in a efficient fashion.</p>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<section id="additions-to-the-object-model">
<h3><a class="toc-backref" href="#additions-to-the-object-model" role="doc-backlink">Additions to the object model</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> and <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> attributes will be added to <code class="docutils literal notranslate"><span class="pre">object</span></code>.
<code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> should be overridden by classes that want to match mapping or sequence patterns.
<code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> should be overridden by classes that want to change the default behavior when matching class patterns.</p>
<p><code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> must be an integer and should be exactly one of these:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="mi">0</span>
<span class="n">MATCH_SEQUENCE</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">MATCH_MAPPING</span> <span class="o">=</span> <span class="mi">2</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">MATCH_SEQUENCE</span></code> is used to indicate that instances of the class can match sequence patterns.</p>
<p><code class="docutils literal notranslate"><span class="pre">MATCH_MAPPING</span></code> is used to indicate that instances of the class can match mapping patterns.</p>
<p><code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> must be an integer and should be exactly one of these:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="mi">0</span>
<span class="n">MATCH_SELF</span> <span class="o">=</span> <span class="mi">8</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">MATCH_SELF</span></code> is used to indicate that for a single positional argument class pattern, the subject will be used and not deconstructed.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>In the rest of this document, we will refer to the above values by name only.
Symbolic constants will be provided both for Python and C, and the values will
never be changed.</p>
</div>
<p><code class="docutils literal notranslate"><span class="pre">object</span></code> will have the following values for the special attributes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">__match_container__</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">__match_class__</span><span class="o">=</span> <span class="mi">0</span>
<span class="n">__match_args__</span> <span class="o">=</span> <span class="p">()</span>
</pre></div>
</div>
<p>These special attributes will be inherited as normal.</p>
<p>If <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> is overridden, then it is required to hold a tuple of unique strings. It may be empty.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p><code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> will be automatically generated for dataclasses and named tuples, as specified in <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a>.</p>
</div>
<p>The pattern matching implementation is <em>not</em> required to check that any of these attributes behave as specified.
If the value of <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code>, <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> or <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> is not as specified, then
the implementation may raise any exception, or match the wrong pattern.
Of course, implementations are free to check these properties and provide meaningful error messages if they can do so efficiently.</p>
</section>
<section id="semantics-of-the-matching-process">
<h3><a class="toc-backref" href="#semantics-of-the-matching-process" role="doc-backlink">Semantics of the matching process</a></h3>
<p>In the following, all variables of the form <code class="docutils literal notranslate"><span class="pre">$var</span></code> are temporary variables and are not visible to the Python program.
They may be visible via introspection, but that is an implementation detail and should not be relied on.
The pseudo-statement <code class="docutils literal notranslate"><span class="pre">FAIL</span></code> is used to signify that matching failed for this pattern and that matching should move to the next pattern.
If control reaches the end of the translation without reaching a <code class="docutils literal notranslate"><span class="pre">FAIL</span></code>, then it has matched, and following patterns are ignored.</p>
<p>Variables of the form <code class="docutils literal notranslate"><span class="pre">$ALL_CAPS</span></code> are meta-variables holding a syntactic element, they are not normal variables.
So, <code class="docutils literal notranslate"><span class="pre">$VARS</span> <span class="pre">=</span> <span class="pre">$items</span></code> is not an assignment of <code class="docutils literal notranslate"><span class="pre">$items</span></code> to <code class="docutils literal notranslate"><span class="pre">$VARS</span></code>,
but an unpacking of <code class="docutils literal notranslate"><span class="pre">$items</span></code> into the variables that <code class="docutils literal notranslate"><span class="pre">$VARS</span></code> holds.
For example, with the abstract syntax <code class="docutils literal notranslate"><span class="pre">case</span> <span class="pre">[$VARS]:</span></code>, and the concrete syntax <code class="docutils literal notranslate"><span class="pre">case[a,</span> <span class="pre">b]:</span></code> then <code class="docutils literal notranslate"><span class="pre">$VARS</span></code> would hold the variables <code class="docutils literal notranslate"><span class="pre">(a,</span> <span class="pre">b)</span></code>,
not the values of those variables.</p>
<p>The pseudo-function <code class="docutils literal notranslate"><span class="pre">QUOTE</span></code> takes a variable and returns the name of that variable.
For example, if the meta-variable <code class="docutils literal notranslate"><span class="pre">$VAR</span></code> held the variable <code class="docutils literal notranslate"><span class="pre">foo</span></code> then <code class="docutils literal notranslate"><span class="pre">QUOTE($VAR)</span> <span class="pre">==</span> <span class="pre">&quot;foo&quot;</span></code>.</p>
<p>All additional code listed below that is not present in the original source will not trigger line events, conforming to <a class="pep reference internal" href="../pep-0626/" title="PEP 626 Precise line numbers for debugging and other tools.">PEP 626</a>.</p>
<section id="preamble">
<h4><a class="toc-backref" href="#preamble" role="doc-backlink">Preamble</a></h4>
<p>Before any patterns are matched, the expression being matched is evaluated:</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>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$value = expr
</pre></div>
</div>
</section>
<section id="capture-patterns">
<h4><a class="toc-backref" href="#capture-patterns" role="doc-backlink">Capture patterns</a></h4>
<p>Capture patterns always match, so the irrefutable match:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">capture_var</span><span class="p">:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>capture_var = $value
</pre></div>
</div>
</section>
<section id="wildcard-patterns">
<h4><a class="toc-backref" href="#wildcard-patterns" role="doc-backlink">Wildcard patterns</a></h4>
<p>Wildcard patterns always match, so:</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="p">:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># No code -- Automatically matches</span>
</pre></div>
</div>
</section>
<section id="literal-patterns">
<h4><a class="toc-backref" href="#literal-patterns" role="doc-backlink">Literal Patterns</a></h4>
<p>The literal pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">LITERAL</span><span class="p">:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if $value != LITERAL:
FAIL
</pre></div>
</div>
<p>except when the literal is one of <code class="docutils literal notranslate"><span class="pre">None</span></code>, <code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code> ,
when it translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if $value is not LITERAL:
FAIL
</pre></div>
</div>
</section>
<section id="value-patterns">
<h4><a class="toc-backref" href="#value-patterns" role="doc-backlink">Value Patterns</a></h4>
<p>The value pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">value</span><span class="o">.</span><span class="n">pattern</span><span class="p">:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if $value != value.pattern:
FAIL
</pre></div>
</div>
</section>
<section id="sequence-patterns">
<h4><a class="toc-backref" href="#sequence-patterns" role="doc-backlink">Sequence Patterns</a></h4>
<p>A pattern not including a star pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case [$VARS]:
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_container__
if $kind != MATCH_SEQUENCE:
FAIL
if len($value) != len($VARS):
FAIL
$VARS = $value
</pre></div>
</div>
<p>Example: <a class="footnote-reference brackets" href="#id13" id="id2">[2]</a></p>
<p>A pattern including a star pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case [$VARS]
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_container__
if $kind != MATCH_SEQUENCE:
FAIL
if len($value) &lt; len($VARS):
FAIL
$VARS = $value # Note that $VARS includes a star expression.
</pre></div>
</div>
<p>Example: <a class="footnote-reference brackets" href="#id14" id="id3">[3]</a></p>
</section>
<section id="mapping-patterns">
<h4><a class="toc-backref" href="#mapping-patterns" role="doc-backlink">Mapping Patterns</a></h4>
<p>A pattern not including a double-star pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case {$KEYWORD_PATTERNS}:
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$sentinel = object()
$kind = type($value).__match_container__
if $kind != MATCH_MAPPING:
FAIL
# $KEYWORD_PATTERNS is a meta-variable mapping names to variables.
for $KEYWORD in $KEYWORD_PATTERNS:
$tmp = $value.get(QUOTE($KEYWORD), $sentinel)
if $tmp is $sentinel:
FAIL
$KEYWORD_PATTERNS[$KEYWORD] = $tmp
</pre></div>
</div>
<p>Example: <a class="footnote-reference brackets" href="#id15" id="id4">[4]</a></p>
<p>A pattern including a double-star pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case {$KEYWORD_PATTERNS, **$DOUBLE_STARRED_PATTERN}:
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_container__
if $kind != MATCH_MAPPING:
FAIL
# $KEYWORD_PATTERNS is a meta-variable mapping names to variables.
$tmp = dict($value)
if not $tmp.keys() &gt;= $KEYWORD_PATTERNS.keys():
FAIL:
for $KEYWORD in $KEYWORD_PATTERNS:
$KEYWORD_PATTERNS[$KEYWORD] = $tmp.pop(QUOTE($KEYWORD))
$DOUBLE_STARRED_PATTERN = $tmp
</pre></div>
</div>
<p>Example: <a class="footnote-reference brackets" href="#id16" id="id5">[5]</a></p>
</section>
<section id="class-patterns">
<h4><a class="toc-backref" href="#class-patterns" role="doc-backlink">Class Patterns</a></h4>
<p>Class pattern with no arguments:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">ClsName</span><span class="p">():</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if not isinstance($value, ClsName):
FAIL
</pre></div>
</div>
<p>Class pattern with a single positional pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case ClsName($VAR):
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_class__
if $kind == MATCH_SELF:
if not isinstance($value, ClsName):
FAIL
$VAR = $value
else:
As other positional-only class pattern
</pre></div>
</div>
<p>Positional-only class pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case ClsName($VARS):
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if not isinstance($value, ClsName):
FAIL
$attrs = ClsName.__match_args__
if len($attr) &lt; len($VARS):
raise TypeError(...)
try:
for i, $VAR in enumerate($VARS):
$VAR = getattr($value, $attrs[i])
except AttributeError:
FAIL
</pre></div>
</div>
<p>Example: <a class="footnote-reference brackets" href="#id17" id="id6">[6]</a></p>
<p>Class patterns with all keyword patterns:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case ClsName($KEYWORD_PATTERNS):
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if not isinstance($value, ClsName):
FAIL
try:
for $KEYWORD in $KEYWORD_PATTERNS:
$tmp = getattr($value, QUOTE($KEYWORD))
$KEYWORD_PATTERNS[$KEYWORD] = $tmp
except AttributeError:
FAIL
</pre></div>
</div>
<p>Example: <a class="footnote-reference brackets" href="#id18" id="id7">[7]</a></p>
<p>Class patterns with positional and keyword patterns:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>case ClsName($VARS, $KEYWORD_PATTERNS):
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if not isinstance($value, ClsName):
FAIL
$attrs = ClsName.__match_args__
if len($attr) &lt; len($VARS):
raise TypeError(...)
$pos_attrs = $attrs[:len($VARS)]
try:
for i, $VAR in enumerate($VARS):
$VAR = getattr($value, $attrs[i])
for $KEYWORD in $KEYWORD_PATTERNS:
$name = QUOTE($KEYWORD)
if $name in pos_attrs:
raise TypeError(...)
$KEYWORD_PATTERNS[$KEYWORD] = getattr($value, $name)
except AttributeError:
FAIL
</pre></div>
</div>
<p>Example: <a class="footnote-reference brackets" href="#id19" id="id8">[8]</a></p>
</section>
<section id="nested-patterns">
<h4><a class="toc-backref" href="#nested-patterns" role="doc-backlink">Nested patterns</a></h4>
<p>The above specification assumes that patterns are not nested. For nested patterns
the above translations are applied recursively by introducing temporary capture patterns.</p>
<p>For example, the pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="p">[</span><span class="nb">int</span><span class="p">(),</span> <span class="nb">str</span><span class="p">()]:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_class__
if $kind != MATCH_SEQUENCE:
FAIL
if len($value) != 2:
FAIL
$value_0, $value_1 = $value
#Now match on temporary values
if not isinstance($value_0, int):
FAIL
if not isinstance($value_1, str):
FAIL
</pre></div>
</div>
</section>
<section id="guards">
<h4><a class="toc-backref" href="#guards" role="doc-backlink">Guards</a></h4>
<p>Guards translate to a test following the rest of the translation:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">pattern</span> <span class="k">if</span> <span class="n">guard</span><span class="p">:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">translation</span> <span class="k">for</span> <span class="n">pattern</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">guard</span><span class="p">:</span>
<span class="n">FAIL</span>
</pre></div>
</div>
</section>
<section id="non-conforming-special-attributes">
<h4><a class="toc-backref" href="#non-conforming-special-attributes" role="doc-backlink">Non-conforming special attributes</a></h4>
<p>All classes should ensure that the the values of <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code>, <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code>
and <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> follow the specification.
Therefore, implementations can assume, without checking, that the following are true:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">__match_container__</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">__match_container__</span> <span class="o">==</span> <span class="n">MATCH_SEQUENCE</span> <span class="ow">or</span> <span class="n">__match_container__</span> <span class="o">==</span> <span class="n">MATCH_MAPPING</span>
<span class="n">__match_class__</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">__match_class__</span> <span class="o">==</span> <span class="n">MATCH_SELF</span>
</pre></div>
</div>
<p>and that <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> is a tuple of unique strings.</p>
</section>
</section>
<section id="values-of-the-special-attributes-for-classes-in-the-standard-library">
<h3><a class="toc-backref" href="#values-of-the-special-attributes-for-classes-in-the-standard-library" role="doc-backlink">Values of the special attributes for classes in the standard library</a></h3>
<p>For the core builtin container classes <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> will be:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">list</span></code>: <code class="docutils literal notranslate"><span class="pre">MATCH_SEQUENCE</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">tuple</span></code>: <code class="docutils literal notranslate"><span class="pre">MATCH_SEQUENCE</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">dict</span></code>: <code class="docutils literal notranslate"><span class="pre">MATCH_MAPPING</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">bytearray</span></code>: 0</li>
<li><code class="docutils literal notranslate"><span class="pre">bytes</span></code>: 0</li>
<li><code class="docutils literal notranslate"><span class="pre">str</span></code>: 0</li>
</ul>
<p>Named tuples will have <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> set to <code class="docutils literal notranslate"><span class="pre">MATCH_SEQUENCE</span></code>.</p>
<ul class="simple">
<li>All other standard library classes for which <code class="docutils literal notranslate"><span class="pre">issubclass(cls,</span> <span class="pre">collections.abc.Mapping)</span></code> is true will have <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> set to <code class="docutils literal notranslate"><span class="pre">MATCH_MAPPING</span></code>.</li>
<li>All other standard library classes for which <code class="docutils literal notranslate"><span class="pre">issubclass(cls,</span> <span class="pre">collections.abc.Sequence)</span></code> is true will have <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> set to <code class="docutils literal notranslate"><span class="pre">MATCH_SEQUENCE</span></code>.</li>
</ul>
<p>For the following builtin classes <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> will be set to <code class="docutils literal notranslate"><span class="pre">MATCH_SELF</span></code>:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">bool</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">bytearray</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">bytes</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">float</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">frozenset</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">int</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">set</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">str</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">list</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">tuple</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">dict</span></code></li>
</ul>
</section>
<section id="legal-optimizations">
<h3><a class="toc-backref" href="#legal-optimizations" role="doc-backlink">Legal optimizations</a></h3>
<p>The above semantics implies a lot of redundant effort and copying in the implementation.
However, it is possible to implement the above semantics efficiently by employing semantic preserving transformations
on the naive implementation.</p>
<p>When performing matching, implementations are allowed
to treat the following functions and methods as pure:</p>
<p>For any class supporting <code class="docutils literal notranslate"><span class="pre">MATCH_SEQUENCE</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>* ``cls.__len__()``
* ``cls.__getitem__()``
</pre></div>
</div>
<p>For any class supporting <code class="docutils literal notranslate"><span class="pre">MATCH_MAPPING</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>* ``cls.get()`` (Two argument form only)
</pre></div>
</div>
<p>Implementations are allowed to make the following assumptions:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">cls)</span></code> can be freely replaced with <code class="docutils literal notranslate"><span class="pre">issubclass(type(obj),</span> <span class="pre">cls)</span></code> and vice-versa.</li>
<li><code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">cls)</span></code> will always return the same result for any <code class="docutils literal notranslate"><span class="pre">(obj,</span> <span class="pre">cls)</span></code> pair and repeated calls can thus be elided.</li>
<li>Reading any of <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code>, <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> or <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> is a pure operation, and may be cached.</li>
<li>Sequences, that is any class for which <code class="docutils literal notranslate"><span class="pre">__match_container__</span> <span class="pre">==</span> <span class="pre">MATCH_SEQUENCE</span></code> is not zero, are not modified by iteration, subscripting or calls to <code class="docutils literal notranslate"><span class="pre">len()</span></code>.
Consequently, those operations can be freely substituted for each other where they would be equivalent when applied to an immutable sequence.</li>
<li>Mappings, that is any class for which <code class="docutils literal notranslate"><span class="pre">__match_container__</span> <span class="pre">==</span> <span class="pre">MATCH_MAPPING</span></code> is not zero, will not capture the second argument of the <code class="docutils literal notranslate"><span class="pre">get()</span></code> method.
So, the <code class="docutils literal notranslate"><span class="pre">$sentinel</span></code> value may be freely re-used.</li>
</ul>
<p>In fact, implementations are encouraged to make these assumptions, as it is likely to result in significantly better performance.</p>
</section>
</section>
<section id="security-implications">
<h2><a class="toc-backref" href="#security-implications" role="doc-backlink">Security Implications</a></h2>
<p>None.</p>
</section>
<section id="implementation">
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
<p>The naive implementation that follows from the specification will not be very efficient.
Fortunately, there are some reasonably straightforward transformations that can be used to improve performance.
Performance should be comparable to the implementation of <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a> (at time of writing) by the release of 3.10.
Further performance improvements may have to wait for the 3.11 release.</p>
<section id="possible-optimizations">
<h3><a class="toc-backref" href="#possible-optimizations" role="doc-backlink">Possible optimizations</a></h3>
<p>The following is not part of the specification,
but guidelines to help developers create an efficient implementation.</p>
<section id="splitting-evaluation-into-lanes">
<h4><a class="toc-backref" href="#splitting-evaluation-into-lanes" role="doc-backlink">Splitting evaluation into lanes</a></h4>
<p>Since the first step in matching each pattern is check to against the kind, it is possible to combine all the checks against kind into a single multi-way branch at the beginning
of the match. The list of cases can then be duplicated into several “lanes” each corresponding to one kind.
It is then trivial to remove unmatchable cases from each lane.
Depending on the kind, different optimization strategies are possible for each lane.
Note that the body of the match clause does not need to be duplicated, just the pattern.</p>
</section>
<section id="id9">
<h4><a class="toc-backref" href="#id9" role="doc-backlink">Sequence patterns</a></h4>
<p>This is probably the most complex to optimize and the most profitable in terms of performance.
Since each pattern can only match a range of lengths, often only a single length,
the sequence of tests can be rewritten in as an explicit iteration over the sequence,
attempting to match only those patterns that apply to that sequence length.</p>
<p>For example:</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="k">case</span> <span class="p">[</span><span class="n">x</span><span class="p">]:</span>
<span class="n">B</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">C</span>
<span class="k">case</span> <span class="n">other</span><span class="p">:</span>
<span class="n">D</span>
</pre></div>
</div>
<p>Can be compiled roughly as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> # Choose lane
$i = iter($value)
for $0 in $i:
break
else:
A
goto done
for $1 in $i:
break
else:
x = $0
B
goto done
for $2 in $i:
del $0, $1, $2
break
else:
x = $0
y = $1
C
goto done
other = $value
D
done:
</pre></div>
</div>
</section>
<section id="id10">
<h4><a class="toc-backref" href="#id10" role="doc-backlink">Mapping patterns</a></h4>
<p>The best strategy here is probably to form a decision tree based on the size of the mapping and which keys are present.
There is no point repeatedly testing for the presence of a key.
For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">obj</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="n">x</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span><span class="n">y</span><span class="p">}:</span>
<span class="n">W</span>
<span class="k">case</span> <span class="p">{</span><span class="n">a</span><span class="p">:</span><span class="n">x</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span><span class="n">y</span><span class="p">}:</span>
<span class="n">X</span>
<span class="k">case</span><span class="w"> </span><span class="p">{</span><span class="n">a</span><span class="p">:</span><span class="n">x</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span><span class="k">_</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span><span class="n">y</span><span class="p">}:</span>
<span class="n">Y</span>
<span class="k">case</span> <span class="n">other</span><span class="p">:</span>
<span class="n">Z</span>
</pre></div>
</div>
<p>If the key <code class="docutils literal notranslate"><span class="pre">&quot;a&quot;</span></code> is not present when checking for case X, there is no need to check it again for Y.</p>
<p>The mapping lane can be implemented, roughly as:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span># Choose lane
if len($value) == 2:
if &quot;a&quot; in $value:
if &quot;b&quot; in $value:
x = $value[&quot;a&quot;]
y = $value[&quot;b&quot;]
goto W
if &quot;c&quot; in $value:
x = $value[&quot;a&quot;]
y = $value[&quot;c&quot;]
goto X
elif len($value) == 3:
if &quot;a&quot; in $value and &quot;b&quot; in $value:
x = $value[&quot;a&quot;]
y = $value[&quot;c&quot;]
goto Y
other = $value
goto Z
</pre></div>
</div>
</section>
</section>
</section>
<section id="summary-of-differences-between-this-pep-and-pep-634">
<h2><a class="toc-backref" href="#summary-of-differences-between-this-pep-and-pep-634" role="doc-backlink">Summary of differences between this PEP and PEP 634</a></h2>
<p>The changes to the semantics can be summarized as:</p>
<ul class="simple">
<li>Requires <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> to be a <em>tuple</em> of strings, not just a sequence.
This make pattern matching a bit more robust and optimizable as <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> can be assumed to be immutable.</li>
<li>Selecting the kind of container patterns that can be matched uses <code class="docutils literal notranslate"><span class="pre">cls.__match_container__</span></code> instead of
<code class="docutils literal notranslate"><span class="pre">issubclass(cls,</span> <span class="pre">collections.abc.Mapping)</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass(cls,</span> <span class="pre">collections.abc.Sequence)</span></code>.</li>
<li>Allows classes to opt out of deconstruction altogether, if necessary, but setting <code class="docutils literal notranslate"><span class="pre">__match_class__</span> <span class="pre">=</span> <span class="pre">0</span></code>.</li>
<li>The behavior when matching patterns is more precisely defined, but is otherwise unchanged.</li>
</ul>
<p>There are no changes to syntax. All examples given in the <a class="pep reference internal" href="../pep-0636/" title="PEP 636 Structural Pattern Matching: Tutorial">PEP 636</a> tutorial should continue to work as they do now.</p>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="using-attributes-from-the-instance-s-dictionary">
<h3><a class="toc-backref" href="#using-attributes-from-the-instance-s-dictionary" role="doc-backlink">Using attributes from the instances dictionary</a></h3>
<p>An earlier version of this PEP only used attributes from the instances dictionary when matching a class pattern with <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> was the default value.
The intent was to avoid capturing bound-methods and other synthetic attributes. However, this also mean that properties were ignored.</p>
<p>For the class:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="s2">&quot;a&quot;</span>
<span class="nd">@property</span>
<span class="k">def</span> <span class="nf">p</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">m</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Ideally we would match the attributes “a” and “p”, but not “m”.
However, there is no general way to do that, so this PEP now follows the semantics of <a class="pep reference internal" href="../pep-0634/" title="PEP 634 Structural Pattern Matching: Specification">PEP 634</a>.</p>
</section>
<section id="lookup-of-match-args-on-the-subject-not-the-pattern">
<h3><a class="toc-backref" href="#lookup-of-match-args-on-the-subject-not-the-pattern" role="doc-backlink">Lookup of <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> on the subject not the pattern</a></h3>
<p>An earlier version of this PEP looked up <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> on the class of the subject and
not the class specified in the pattern.
This has been rejected for a few reasons:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">*</span> <span class="n">Using</span> <span class="n">the</span> <span class="k">class</span> <span class="nc">specified</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">pattern</span> <span class="ow">is</span> <span class="n">more</span> <span class="n">amenable</span> <span class="n">to</span> <span class="n">optimization</span> <span class="ow">and</span> <span class="n">can</span> <span class="n">offer</span> <span class="n">better</span> <span class="n">performance</span><span class="o">.</span>
<span class="o">*</span> <span class="n">Using</span> <span class="n">the</span> <span class="k">class</span> <span class="nc">specified</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">pattern</span> <span class="n">has</span> <span class="n">the</span> <span class="n">potential</span> <span class="n">to</span> <span class="n">provide</span> <span class="n">better</span> <span class="n">error</span> <span class="n">reporting</span> <span class="ow">is</span> <span class="n">some</span> <span class="n">cases</span><span class="o">.</span>
<span class="o">*</span> <span class="n">Neither</span> <span class="n">approach</span> <span class="ow">is</span> <span class="n">perfect</span><span class="p">,</span> <span class="n">both</span> <span class="n">have</span> <span class="n">odd</span> <span class="n">corner</span> <span class="n">cases</span><span class="o">.</span> <span class="n">Keeping</span> <span class="n">the</span> <span class="n">status</span> <span class="n">quo</span> <span class="n">minimizes</span> <span class="n">disruption</span><span class="o">.</span>
</pre></div>
</div>
</section>
<section id="combining-match-class-and-match-container-into-a-single-value">
<h3><a class="toc-backref" href="#combining-match-class-and-match-container-into-a-single-value" role="doc-backlink">Combining <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> and <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> into a single value</a></h3>
<p>An earlier version of this PEP combined <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> and <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> into a single value, <code class="docutils literal notranslate"><span class="pre">__match_kind__</span></code>.
Using a single value has a small advantage in terms of performance,
but is likely to result in unintended changes to container matching when overriding class matching behavior, and vice versa.</p>
</section>
</section>
<section id="deferred-ideas">
<h2><a class="toc-backref" href="#deferred-ideas" role="doc-backlink">Deferred Ideas</a></h2>
<p>The original version of this PEP included the match kind <code class="docutils literal notranslate"><span class="pre">MATCH_POSITIONAL</span></code> and special method
<code class="docutils literal notranslate"><span class="pre">__deconstruct__</span></code> which would allow classes full control over their matching. This is important
for libraries like <code class="docutils literal notranslate"><span class="pre">sympy</span></code>.</p>
<p>For example, using <code class="docutils literal notranslate"><span class="pre">sympy</span></code>, we might want to write:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># sin(x)**2 + cos(x)**2 == 1</span>
<span class="k">case</span> <span class="n">Add</span><span class="p">(</span><span class="n">Pow</span><span class="p">(</span><span class="n">sin</span><span class="p">(</span><span class="n">a</span><span class="p">),</span> <span class="mi">2</span><span class="p">),</span> <span class="n">Pow</span><span class="p">(</span><span class="n">cos</span><span class="p">(</span><span class="n">b</span><span class="p">),</span> <span class="mi">2</span><span class="p">))</span> <span class="k">if</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">1</span>
</pre></div>
</div>
<p>For <code class="docutils literal notranslate"><span class="pre">sympy</span></code> to support the positional patterns with current pattern matching is possible,
but is tricky. With these additional features it can be implemented easily <a class="footnote-reference brackets" href="#id20" id="id11">[9]</a>.</p>
<p>This idea will feature in a future PEP for 3.11.
However, it is too late in the 3.10 development cycle for such a change.</p>
<section id="having-a-separate-value-to-reject-all-class-matches">
<h3><a class="toc-backref" href="#having-a-separate-value-to-reject-all-class-matches" role="doc-backlink">Having a separate value to reject all class matches</a></h3>
<p>In an earlier version of this PEP, there was a distinct value for <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> that allowed classes to not match any class
pattern that would have required deconstruction. However, this would become redundant once <code class="docutils literal notranslate"><span class="pre">MATCH_POSITIONAL</span></code> is introduced, and
complicates the specification for an extremely rare case.</p>
</section>
</section>
<section id="code-examples">
<h2><a class="toc-backref" href="#code-examples" role="doc-backlink">Code examples</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id12" role="doc-footnote">
<dt class="label" id="id12">[<a href="#id1">1</a>]</dt>
<dd></aside>
</aside>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Symbol</span><span class="p">:</span>
<span class="n">__match_class__</span> <span class="o">=</span> <span class="n">MATCH_SELF</span>
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id13" role="doc-footnote">
<dt class="label" id="id13">[<a href="#id2">2</a>]</dt>
<dd></aside>
</aside>
<p>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">a</span><span class="p">,</span> <span class="n">b</span><span class="p">]</span> <span class="k">if</span> <span class="n">a</span> <span class="ow">is</span> <span class="n">b</span><span class="p">:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_container__
if $kind != MATCH_SEQUENCE:
FAIL
if len($value) != 2:
FAIL
a, b = $value
if not a is b:
FAIL
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id14" role="doc-footnote">
<dt class="label" id="id14">[<a href="#id3">3</a>]</dt>
<dd></aside>
</aside>
<p>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">a</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">]:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_container__
if $kind != MATCH_SEQUENCE:
FAIL
if len($value) &lt; 2:
FAIL
a, *b, c = $value
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id15" role="doc-footnote">
<dt class="label" id="id15">[<a href="#id4">4</a>]</dt>
<dd></aside>
</aside>
<p>This:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="p">{</span><span class="s2">&quot;x&quot;</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="s2">&quot;y&quot;</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">&gt;</span> <span class="mi">2</span><span class="p">:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_container__
if $kind != MATCH_MAPPING:
FAIL
$tmp = $value.get(&quot;x&quot;, $sentinel)
if $tmp is $sentinel:
FAIL
x = $tmp
$tmp = $value.get(&quot;y&quot;, $sentinel)
if $tmp is $sentinel:
FAIL
y = $tmp
if not x &gt; 2:
FAIL
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id16" role="doc-footnote">
<dt class="label" id="id16">[<a href="#id5">5</a>]</dt>
<dd></aside>
</aside>
<p>This:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="p">{</span><span class="s2">&quot;x&quot;</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="s2">&quot;y&quot;</span><span class="p">:</span> <span class="n">y</span><span class="p">,</span> <span class="o">**</span><span class="n">z</span><span class="p">}:</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$kind = type($value).__match_container__
if $kind != MATCH_MAPPING:
FAIL
$tmp = dict($value)
if not $tmp.keys() &gt;= {&quot;x&quot;, &quot;y&quot;}:
FAIL
x = $tmp.pop(&quot;x&quot;)
y = $tmp.pop(&quot;y&quot;)
z = $tmp
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id17" role="doc-footnote">
<dt class="label" id="id17">[<a href="#id6">6</a>]</dt>
<dd></aside>
</aside>
<p>This:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">ClsName</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>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if not isinstance($value, ClsName):
FAIL
$attrs = ClsName.__match_args__
if len($attr) &lt; 2:
FAIL
try:
x = getattr($value, $attrs[0])
y = getattr($value, $attrs[1])
except AttributeError:
FAIL
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id18" role="doc-footnote">
<dt class="label" id="id18">[<a href="#id7">7</a>]</dt>
<dd></aside>
</aside>
<p>This:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">ClsName</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="n">x</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="n">y</span><span class="p">):</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if not isinstance($value, ClsName):
FAIL
try:
x = $value.a
y = $value.b
except AttributeError:
FAIL
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id19" role="doc-footnote">
<dt class="label" id="id19">[<a href="#id8">8</a>]</dt>
<dd></aside>
</aside>
<p>This:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">match</span> <span class="n">ClsName</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="n">y</span><span class="p">):</span>
</pre></div>
</div>
<p>translates to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>if not isinstance($value, ClsName):
FAIL
$attrs = ClsName.__match_args__
if len($attr) &lt; 1:
raise TypeError(...)
$positional_names = $attrs[:1]
try:
x = getattr($value, $attrs[0])
if &quot;a&quot; in $positional_names:
raise TypeError(...)
y = $value.a
except AttributeError:
FAIL
</pre></div>
</div>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id20" role="doc-footnote">
<dt class="label" id="id20">[<a href="#id11">9</a>]</dt>
<dd></aside>
</aside>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Basic</span><span class="p">:</span>
<span class="n">__match_class__</span> <span class="o">=</span> <span class="n">MATCH_POSITIONAL</span>
<span class="k">def</span> <span class="nf">__deconstruct__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_args</span>
</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-0653.rst">https://github.com/python/peps/blob/main/peps/pep-0653.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0653.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></li>
<li><a class="reference internal" href="#motivation">Motivation</a><ul>
<li><a class="reference internal" href="#precise-semantics">Precise semantics</a></li>
<li><a class="reference internal" href="#improved-control-over-class-matching">Improved control over class matching</a></li>
<li><a class="reference internal" href="#robustness">Robustness</a></li>
<li><a class="reference internal" href="#efficient-implementation">Efficient implementation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#additions-to-the-object-model">Additions to the object model</a></li>
<li><a class="reference internal" href="#semantics-of-the-matching-process">Semantics of the matching process</a><ul>
<li><a class="reference internal" href="#preamble">Preamble</a></li>
<li><a class="reference internal" href="#capture-patterns">Capture patterns</a></li>
<li><a class="reference internal" href="#wildcard-patterns">Wildcard patterns</a></li>
<li><a class="reference internal" href="#literal-patterns">Literal Patterns</a></li>
<li><a class="reference internal" href="#value-patterns">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>
<li><a class="reference internal" href="#nested-patterns">Nested patterns</a></li>
<li><a class="reference internal" href="#guards">Guards</a></li>
<li><a class="reference internal" href="#non-conforming-special-attributes">Non-conforming special attributes</a></li>
</ul>
</li>
<li><a class="reference internal" href="#values-of-the-special-attributes-for-classes-in-the-standard-library">Values of the special attributes for classes in the standard library</a></li>
<li><a class="reference internal" href="#legal-optimizations">Legal optimizations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#security-implications">Security Implications</a></li>
<li><a class="reference internal" href="#implementation">Implementation</a><ul>
<li><a class="reference internal" href="#possible-optimizations">Possible optimizations</a><ul>
<li><a class="reference internal" href="#splitting-evaluation-into-lanes">Splitting evaluation into lanes</a></li>
<li><a class="reference internal" href="#id9">Sequence patterns</a></li>
<li><a class="reference internal" href="#id10">Mapping patterns</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#summary-of-differences-between-this-pep-and-pep-634">Summary of differences between this PEP and PEP 634</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#using-attributes-from-the-instance-s-dictionary">Using attributes from the instances dictionary</a></li>
<li><a class="reference internal" href="#lookup-of-match-args-on-the-subject-not-the-pattern">Lookup of <code class="docutils literal notranslate"><span class="pre">__match_args__</span></code> on the subject not the pattern</a></li>
<li><a class="reference internal" href="#combining-match-class-and-match-container-into-a-single-value">Combining <code class="docutils literal notranslate"><span class="pre">__match_class__</span></code> and <code class="docutils literal notranslate"><span class="pre">__match_container__</span></code> into a single value</a></li>
</ul>
</li>
<li><a class="reference internal" href="#deferred-ideas">Deferred Ideas</a><ul>
<li><a class="reference internal" href="#having-a-separate-value-to-reject-all-class-matches">Having a separate value to reject all class matches</a></li>
</ul>
</li>
<li><a class="reference internal" href="#code-examples">Code examples</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-0653.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>