1474 lines
144 KiB
HTML
1474 lines
144 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 572 – Assignment Expressions | peps.python.org</title>
|
|||
|
<link rel="shortcut icon" href="../_static/py.png">
|
|||
|
<link rel="canonical" href="https://peps.python.org/pep-0572/">
|
|||
|
<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 572 – Assignment Expressions | peps.python.org'>
|
|||
|
<meta property="og:description" content="This is a proposal for creating a way to assign to variables within an expression using the notation NAME := expr.">
|
|||
|
<meta property="og:type" content="website">
|
|||
|
<meta property="og:url" content="https://peps.python.org/pep-0572/">
|
|||
|
<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 is a proposal for creating a way to assign to variables within an expression using the notation NAME := expr.">
|
|||
|
<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 572</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 572 – Assignment Expressions</h1>
|
|||
|
<dl class="rfc2822 field-list simple">
|
|||
|
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
|||
|
<dd class="field-odd">Chris Angelico <rosuav at gmail.com>, Tim Peters <tim.peters at gmail.com>,
|
|||
|
Guido van Rossum <guido at python.org></dd>
|
|||
|
<dt class="field-even">Status<span class="colon">:</span></dt>
|
|||
|
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</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">28-Feb-2018</dd>
|
|||
|
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
|||
|
<dd class="field-odd">3.8</dd>
|
|||
|
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
|||
|
<dd class="field-even">28-Feb-2018, 02-Mar-2018, 23-Mar-2018, 04-Apr-2018, 17-Apr-2018,
|
|||
|
25-Apr-2018, 09-Jul-2018, 05-Aug-2019</dd>
|
|||
|
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
|
|||
|
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2018-July/154601.html">Python-Dev message</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></li>
|
|||
|
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
|
|||
|
<li><a class="reference internal" href="#the-importance-of-real-code">The importance of real code</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#syntax-and-semantics">Syntax and semantics</a><ul>
|
|||
|
<li><a class="reference internal" href="#exceptional-cases">Exceptional cases</a></li>
|
|||
|
<li><a class="reference internal" href="#scope-of-the-target">Scope of the target</a></li>
|
|||
|
<li><a class="reference internal" href="#relative-precedence-of">Relative precedence of <code class="docutils literal notranslate"><span class="pre">:=</span></code></a></li>
|
|||
|
<li><a class="reference internal" href="#change-to-evaluation-order">Change to evaluation order</a></li>
|
|||
|
<li><a class="reference internal" href="#differences-between-assignment-expressions-and-assignment-statements">Differences between assignment expressions and assignment statements</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#specification-changes-during-implementation">Specification changes during implementation</a></li>
|
|||
|
<li><a class="reference internal" href="#examples">Examples</a><ul>
|
|||
|
<li><a class="reference internal" href="#examples-from-the-python-standard-library">Examples from the Python standard library</a><ul>
|
|||
|
<li><a class="reference internal" href="#site-py">site.py</a></li>
|
|||
|
<li><a class="reference internal" href="#pydecimal-py">_pydecimal.py</a></li>
|
|||
|
<li><a class="reference internal" href="#copy-py">copy.py</a></li>
|
|||
|
<li><a class="reference internal" href="#datetime-py">datetime.py</a></li>
|
|||
|
<li><a class="reference internal" href="#sysconfig-py">sysconfig.py</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#simplifying-list-comprehensions">Simplifying list comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#capturing-condition-values">Capturing condition values</a></li>
|
|||
|
<li><a class="reference internal" href="#fork">Fork</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#rejected-alternative-proposals">Rejected alternative proposals</a><ul>
|
|||
|
<li><a class="reference internal" href="#changing-the-scope-rules-for-comprehensions">Changing the scope rules for comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#alternative-spellings">Alternative spellings</a></li>
|
|||
|
<li><a class="reference internal" href="#special-casing-conditional-statements">Special-casing conditional statements</a></li>
|
|||
|
<li><a class="reference internal" href="#special-casing-comprehensions">Special-casing comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#lowering-operator-precedence">Lowering operator precedence</a></li>
|
|||
|
<li><a class="reference internal" href="#allowing-commas-to-the-right">Allowing commas to the right</a></li>
|
|||
|
<li><a class="reference internal" href="#always-requiring-parentheses">Always requiring parentheses</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#frequently-raised-objections">Frequently Raised Objections</a><ul>
|
|||
|
<li><a class="reference internal" href="#why-not-just-turn-existing-assignment-into-an-expression">Why not just turn existing assignment into an expression?</a></li>
|
|||
|
<li><a class="reference internal" href="#with-assignment-expressions-why-bother-with-assignment-statements">With assignment expressions, why bother with assignment statements?</a></li>
|
|||
|
<li><a class="reference internal" href="#why-not-use-a-sublocal-scope-and-prevent-namespace-pollution">Why not use a sublocal scope and prevent namespace pollution?</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#style-guide-recommendations">Style guide recommendations</a></li>
|
|||
|
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
|||
|
<li><a class="reference internal" href="#appendix-a-tim-peters-s-findings">Appendix A: Tim Peters’s findings</a><ul>
|
|||
|
<li><a class="reference internal" href="#a-numeric-example">A numeric example</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#appendix-b-rough-code-translations-for-comprehensions">Appendix B: Rough code translations for comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#appendix-c-no-changes-to-scope-semantics">Appendix C: No Changes to Scope Semantics</a></li>
|
|||
|
<li><a class="reference internal" href="#references">References</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 is a proposal for creating a way to assign to variables within an
|
|||
|
expression using the notation <code class="docutils literal notranslate"><span class="pre">NAME</span> <span class="pre">:=</span> <span class="pre">expr</span></code>.</p>
|
|||
|
<p>As part of this change, there is also an update to dictionary comprehension
|
|||
|
evaluation order to ensure key expressions are executed before value
|
|||
|
expressions (allowing the key to be bound to a name and then re-used as part of
|
|||
|
calculating the corresponding value).</p>
|
|||
|
<p>During discussion of this PEP, the operator became informally known as
|
|||
|
“the walrus operator”. The construct’s formal name is “Assignment Expressions”
|
|||
|
(as per the PEP title), but they may also be referred to as “Named Expressions”
|
|||
|
(e.g. the CPython reference implementation uses that name internally).</p>
|
|||
|
</section>
|
|||
|
<section id="rationale">
|
|||
|
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
|||
|
<p>Naming the result of an expression is an important part of programming,
|
|||
|
allowing a descriptive name to be used in place of a longer expression,
|
|||
|
and permitting reuse. Currently, this feature is available only in
|
|||
|
statement form, making it unavailable in list comprehensions and other
|
|||
|
expression contexts.</p>
|
|||
|
<p>Additionally, naming sub-parts of a large expression can assist an interactive
|
|||
|
debugger, providing useful display hooks and partial results. Without a way to
|
|||
|
capture sub-expressions inline, this would require refactoring of the original
|
|||
|
code; with assignment expressions, this merely requires the insertion of a few
|
|||
|
<code class="docutils literal notranslate"><span class="pre">name</span> <span class="pre">:=</span></code> markers. Removing the need to refactor reduces the likelihood that
|
|||
|
the code be inadvertently changed as part of debugging (a common cause of
|
|||
|
Heisenbugs), and is easier to dictate to another programmer.</p>
|
|||
|
<section id="the-importance-of-real-code">
|
|||
|
<h3><a class="toc-backref" href="#the-importance-of-real-code" role="doc-backlink">The importance of real code</a></h3>
|
|||
|
<p>During the development of this PEP many people (supporters and critics
|
|||
|
both) have had a tendency to focus on toy examples on the one hand,
|
|||
|
and on overly complex examples on the other.</p>
|
|||
|
<p>The danger of toy examples is twofold: they are often too abstract to
|
|||
|
make anyone go “ooh, that’s compelling”, and they are easily refuted
|
|||
|
with “I would never write it that way anyway”.</p>
|
|||
|
<p>The danger of overly complex examples is that they provide a
|
|||
|
convenient strawman for critics of the proposal to shoot down (“that’s
|
|||
|
obfuscated”).</p>
|
|||
|
<p>Yet there is some use for both extremely simple and extremely complex
|
|||
|
examples: they are helpful to clarify the intended semantics.
|
|||
|
Therefore, there will be some of each below.</p>
|
|||
|
<p>However, in order to be <em>compelling</em>, examples should be rooted in
|
|||
|
real code, i.e. code that was written without any thought of this PEP,
|
|||
|
as part of a useful application, however large or small. Tim Peters
|
|||
|
has been extremely helpful by going over his own personal code
|
|||
|
repository and picking examples of code he had written that (in his
|
|||
|
view) would have been <em>clearer</em> if rewritten with (sparing) use of
|
|||
|
assignment expressions. His conclusion: the current proposal would
|
|||
|
have allowed a modest but clear improvement in quite a few bits of
|
|||
|
code.</p>
|
|||
|
<p>Another use of real code is to observe indirectly how much value
|
|||
|
programmers place on compactness. Guido van Rossum searched through a
|
|||
|
Dropbox code base and discovered some evidence that programmers value
|
|||
|
writing fewer lines over shorter lines.</p>
|
|||
|
<p>Case in point: Guido found several examples where a programmer
|
|||
|
repeated a subexpression, slowing down the program, in order to save
|
|||
|
one line of code, e.g. instead of writing:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
|||
|
<span class="n">group</span> <span class="o">=</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="n">match</span> <span class="k">else</span> <span class="kc">None</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>they would write:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">group</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Another example illustrates that programmers sometimes do more work to
|
|||
|
save an extra level of indentation:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">match1</span> <span class="o">=</span> <span class="n">pattern1</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
|||
|
<span class="n">match2</span> <span class="o">=</span> <span class="n">pattern2</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">match1</span><span class="p">:</span>
|
|||
|
<span class="n">result</span> <span class="o">=</span> <span class="n">match1</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
|||
|
<span class="k">elif</span> <span class="n">match2</span><span class="p">:</span>
|
|||
|
<span class="n">result</span> <span class="o">=</span> <span class="n">match2</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">result</span> <span class="o">=</span> <span class="kc">None</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This code tries to match <code class="docutils literal notranslate"><span class="pre">pattern2</span></code> even if <code class="docutils literal notranslate"><span class="pre">pattern1</span></code> has a match
|
|||
|
(in which case the match on <code class="docutils literal notranslate"><span class="pre">pattern2</span></code> is never used). The more
|
|||
|
efficient rewrite would have been:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">match1</span> <span class="o">=</span> <span class="n">pattern1</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">match1</span><span class="p">:</span>
|
|||
|
<span class="n">result</span> <span class="o">=</span> <span class="n">match1</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">match2</span> <span class="o">=</span> <span class="n">pattern2</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">match2</span><span class="p">:</span>
|
|||
|
<span class="n">result</span> <span class="o">=</span> <span class="n">match2</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">result</span> <span class="o">=</span> <span class="kc">None</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="syntax-and-semantics">
|
|||
|
<h2><a class="toc-backref" href="#syntax-and-semantics" role="doc-backlink">Syntax and semantics</a></h2>
|
|||
|
<p>In most contexts where arbitrary Python expressions can be used, a
|
|||
|
<strong>named expression</strong> can appear. This is of the form <code class="docutils literal notranslate"><span class="pre">NAME</span> <span class="pre">:=</span> <span class="pre">expr</span></code>
|
|||
|
where <code class="docutils literal notranslate"><span class="pre">expr</span></code> is any valid Python expression other than an
|
|||
|
unparenthesized tuple, and <code class="docutils literal notranslate"><span class="pre">NAME</span></code> is an identifier.</p>
|
|||
|
<p>The value of such a named expression is the same as the incorporated
|
|||
|
expression, with the additional side-effect that the target is assigned
|
|||
|
that value:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Handle a matched regex</span>
|
|||
|
<span class="k">if</span> <span class="p">(</span><span class="n">match</span> <span class="o">:=</span> <span class="n">pattern</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">data</span><span class="p">))</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
|||
|
<span class="c1"># Do something with match</span>
|
|||
|
|
|||
|
<span class="c1"># A loop that can't be trivially rewritten using 2-arg iter()</span>
|
|||
|
<span class="k">while</span> <span class="n">chunk</span> <span class="o">:=</span> <span class="n">file</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">8192</span><span class="p">):</span>
|
|||
|
<span class="n">process</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="c1"># Reuse a value that's expensive to compute</span>
|
|||
|
<span class="p">[</span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">y</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span> <span class="n">y</span><span class="o">**</span><span class="mi">3</span><span class="p">]</span>
|
|||
|
|
|||
|
<span class="c1"># Share a subexpression between a comprehension filter clause and its output</span>
|
|||
|
<span class="n">filtered_data</span> <span class="o">=</span> <span class="p">[</span><span class="n">y</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">data</span> <span class="k">if</span> <span class="p">(</span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<section id="exceptional-cases">
|
|||
|
<h3><a class="toc-backref" href="#exceptional-cases" role="doc-backlink">Exceptional cases</a></h3>
|
|||
|
<p>There are a few places where assignment expressions are not allowed,
|
|||
|
in order to avoid ambiguities or user confusion:</p>
|
|||
|
<ul>
|
|||
|
<li>Unparenthesized assignment expressions are prohibited at the top
|
|||
|
level of an expression statement. Example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="p">(</span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="c1"># Valid, though not recommended</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This rule is included to simplify the choice for the user between an
|
|||
|
assignment statement and an assignment expression – there is no
|
|||
|
syntactic position where both are valid.</p>
|
|||
|
</li>
|
|||
|
<li>Unparenthesized assignment expressions are prohibited at the top
|
|||
|
level of the right hand side of an assignment statement. Example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">y0</span> <span class="o">=</span> <span class="n">y1</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="n">y0</span> <span class="o">=</span> <span class="p">(</span><span class="n">y1</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="c1"># Valid, though discouraged</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Again, this rule is included to avoid two visually similar ways of
|
|||
|
saying the same thing.</p>
|
|||
|
</li>
|
|||
|
<li>Unparenthesized assignment expressions are prohibited for the value
|
|||
|
of a keyword argument in a call. Example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">foo</span><span class="p">(</span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="n">foo</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="p">(</span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)))</span> <span class="c1"># Valid, though probably confusing</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This rule is included to disallow excessively confusing code, and
|
|||
|
because parsing keyword arguments is complex enough already.</p>
|
|||
|
</li>
|
|||
|
<li>Unparenthesized assignment expressions are prohibited at the top
|
|||
|
level of a function default value. Example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">answer</span> <span class="o">=</span> <span class="n">p</span> <span class="o">:=</span> <span class="mi">42</span><span class="p">):</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="o">...</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">answer</span><span class="o">=</span><span class="p">(</span><span class="n">p</span> <span class="o">:=</span> <span class="mi">42</span><span class="p">)):</span> <span class="c1"># Valid, though not great style</span>
|
|||
|
<span class="o">...</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This rule is included to discourage side effects in a position whose
|
|||
|
exact semantics are already confusing to many users (cf. the common
|
|||
|
style recommendation against mutable default values), and also to
|
|||
|
echo the similar prohibition in calls (the previous bullet).</p>
|
|||
|
</li>
|
|||
|
<li>Unparenthesized assignment expressions are prohibited as annotations
|
|||
|
for arguments, return values and assignments. Example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">answer</span><span class="p">:</span> <span class="n">p</span> <span class="o">:=</span> <span class="mi">42</span> <span class="o">=</span> <span class="mi">5</span><span class="p">):</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="o">...</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">answer</span><span class="p">:</span> <span class="p">(</span><span class="n">p</span> <span class="o">:=</span> <span class="mi">42</span><span class="p">)</span> <span class="o">=</span> <span class="mi">5</span><span class="p">):</span> <span class="c1"># Valid, but probably never useful</span>
|
|||
|
<span class="o">...</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The reasoning here is similar to the two previous cases; this
|
|||
|
ungrouped assortment of symbols and operators composed of <code class="docutils literal notranslate"><span class="pre">:</span></code> and
|
|||
|
<code class="docutils literal notranslate"><span class="pre">=</span></code> is hard to read correctly.</p>
|
|||
|
</li>
|
|||
|
<li>Unparenthesized assignment expressions are prohibited in lambda functions.
|
|||
|
Example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">x</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="k">lambda</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># Valid, but unlikely to be useful</span>
|
|||
|
<span class="p">(</span><span class="n">x</span> <span class="o">:=</span> <span class="k">lambda</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># Valid</span>
|
|||
|
<span class="k">lambda</span> <span class="n">line</span><span class="p">:</span> <span class="p">(</span><span class="n">m</span> <span class="o">:=</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="n">line</span><span class="p">))</span> <span class="ow">and</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Valid</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This allows <code class="docutils literal notranslate"><span class="pre">lambda</span></code> to always bind less tightly than <code class="docutils literal notranslate"><span class="pre">:=</span></code>; having a
|
|||
|
name binding at the top level inside a lambda function is unlikely to be of
|
|||
|
value, as there is no way to make use of it. In cases where the name will be
|
|||
|
used more than once, the expression is likely to need parenthesizing anyway,
|
|||
|
so this prohibition will rarely affect code.</p>
|
|||
|
</li>
|
|||
|
<li>Assignment expressions inside of f-strings require parentheses. Example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="sa">f</span><span class="s1">'</span><span class="si">{</span><span class="p">(</span><span class="n">x</span><span class="o">:=</span><span class="mi">10</span><span class="p">)</span><span class="si">}</span><span class="s1">'</span> <span class="c1"># Valid, uses assignment expression</span>
|
|||
|
<span class="go">'10'</span>
|
|||
|
<span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="mi">10</span>
|
|||
|
<span class="gp">>>> </span><span class="sa">f</span><span class="s1">'</span><span class="si">{</span><span class="n">x</span><span class="si">:</span><span class="s1">=10</span><span class="si">}</span><span class="s1">'</span> <span class="c1"># Valid, passes '=10' to formatter</span>
|
|||
|
<span class="go">' 10'</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This shows that what looks like an assignment operator in an f-string is
|
|||
|
not always an assignment operator. The f-string parser uses <code class="docutils literal notranslate"><span class="pre">:</span></code> to
|
|||
|
indicate formatting options. To preserve backwards compatibility,
|
|||
|
assignment operator usage inside of f-strings must be parenthesized.
|
|||
|
As noted above, this usage of the assignment operator is not recommended.</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="scope-of-the-target">
|
|||
|
<h3><a class="toc-backref" href="#scope-of-the-target" role="doc-backlink">Scope of the target</a></h3>
|
|||
|
<p>An assignment expression does not introduce a new scope. In most
|
|||
|
cases the scope in which the target will be bound is self-explanatory:
|
|||
|
it is the current scope. If this scope contains a <code class="docutils literal notranslate"><span class="pre">nonlocal</span></code> or
|
|||
|
<code class="docutils literal notranslate"><span class="pre">global</span></code> declaration for the target, the assignment expression
|
|||
|
honors that. A lambda (being an explicit, if anonymous, function
|
|||
|
definition) counts as a scope for this purpose.</p>
|
|||
|
<p>There is one special case: an assignment expression occurring in a
|
|||
|
list, set or dict comprehension or in a generator expression (below
|
|||
|
collectively referred to as “comprehensions”) binds the target in the
|
|||
|
containing scope, honoring a <code class="docutils literal notranslate"><span class="pre">nonlocal</span></code> or <code class="docutils literal notranslate"><span class="pre">global</span></code> declaration
|
|||
|
for the target in that scope, if one exists. For the purpose of this
|
|||
|
rule the containing scope of a nested comprehension is the scope that
|
|||
|
contains the outermost comprehension. A lambda counts as a containing
|
|||
|
scope.</p>
|
|||
|
<p>The motivation for this special case is twofold. First, it allows us
|
|||
|
to conveniently capture a “witness” for an <code class="docutils literal notranslate"><span class="pre">any()</span></code> expression, or a
|
|||
|
counterexample for <code class="docutils literal notranslate"><span class="pre">all()</span></code>, for example:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="nb">any</span><span class="p">((</span><span class="n">comment</span> <span class="o">:=</span> <span class="n">line</span><span class="p">)</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">'#'</span><span class="p">)</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"First comment:"</span><span class="p">,</span> <span class="n">comment</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"There are no comments"</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="k">if</span> <span class="nb">all</span><span class="p">((</span><span class="n">nonblank</span> <span class="o">:=</span> <span class="n">line</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">==</span> <span class="s1">''</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"All lines are blank"</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"First non-blank line:"</span><span class="p">,</span> <span class="n">nonblank</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Second, it allows a compact way of updating mutable state from a
|
|||
|
comprehension, for example:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Compute partial sums in a list comprehension</span>
|
|||
|
<span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
<span class="n">partial_sums</span> <span class="o">=</span> <span class="p">[</span><span class="n">total</span> <span class="o">:=</span> <span class="n">total</span> <span class="o">+</span> <span class="n">v</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"Total:"</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>However, an assignment expression target name cannot be the same as a
|
|||
|
<code class="docutils literal notranslate"><span class="pre">for</span></code>-target name appearing in any comprehension containing the
|
|||
|
assignment expression. The latter names are local to the
|
|||
|
comprehension in which they appear, so it would be contradictory for a
|
|||
|
contained use of the same name to refer to the scope containing the
|
|||
|
outermost comprehension instead.</p>
|
|||
|
<p>For example, <code class="docutils literal notranslate"><span class="pre">[i</span> <span class="pre">:=</span> <span class="pre">i+1</span> <span class="pre">for</span> <span class="pre">i</span> <span class="pre">in</span> <span class="pre">range(5)]</span></code> is invalid: the <code class="docutils literal notranslate"><span class="pre">for</span>
|
|||
|
<span class="pre">i</span></code> part establishes that <code class="docutils literal notranslate"><span class="pre">i</span></code> is local to the comprehension, but the
|
|||
|
<code class="docutils literal notranslate"><span class="pre">i</span> <span class="pre">:=</span></code> part insists that <code class="docutils literal notranslate"><span class="pre">i</span></code> is not local to the comprehension.
|
|||
|
The same reason makes these examples invalid too:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[[(</span><span class="n">j</span> <span class="o">:=</span> <span class="n">j</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="p">[</span><span class="n">i</span> <span class="o">:=</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">stuff</span><span class="p">]</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">(</span><span class="n">i</span> <span class="o">:=</span> <span class="n">stuff</span><span class="p">)]</span> <span class="c1"># INVALID</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>While it’s technically possible to assign consistent semantics to these cases,
|
|||
|
it’s difficult to determine whether those semantics actually make <em>sense</em> in the
|
|||
|
absence of real use cases. Accordingly, the reference implementation <a class="footnote-reference brackets" href="#id4" id="id1">[1]</a> will ensure
|
|||
|
that such cases raise <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code>, rather than executing with implementation
|
|||
|
defined behaviour.</p>
|
|||
|
<p>This restriction applies even if the assignment expression is never executed:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="kc">False</span> <span class="ow">and</span> <span class="p">(</span><span class="n">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">stuff</span><span class="p">]</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">stuff</span> <span class="k">if</span> <span class="kc">True</span> <span class="ow">or</span> <span class="p">(</span><span class="n">j</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">)]</span> <span class="c1"># INVALID</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>For the comprehension body (the part before the first “for” keyword) and the
|
|||
|
filter expression (the part after “if” and before any nested “for”), this
|
|||
|
restriction applies solely to target names that are also used as iteration
|
|||
|
variables in the comprehension. Lambda expressions appearing in these
|
|||
|
positions introduce a new explicit function scope, and hence may use assignment
|
|||
|
expressions with no additional restrictions.</p>
|
|||
|
<p>Due to design constraints in the reference implementation (the symbol table
|
|||
|
analyser cannot easily detect when names are re-used between the leftmost
|
|||
|
comprehension iterable expression and the rest of the comprehension), named
|
|||
|
expressions are disallowed entirely as part of comprehension iterable
|
|||
|
expressions (the part after each “in”, and before any subsequent “if” or
|
|||
|
“for” keyword):</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">(</span><span class="n">j</span> <span class="o">:=</span> <span class="n">stuff</span><span class="p">)]</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="p">(</span><span class="n">k</span> <span class="o">:=</span> <span class="n">stuff</span><span class="p">)]</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="n">j</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="p">(</span><span class="n">k</span> <span class="o">:=</span> <span class="n">stuff</span><span class="p">)]]</span> <span class="c1"># INVALID</span>
|
|||
|
<span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="p">(</span><span class="n">j</span> <span class="o">:=</span> <span class="n">stuff</span><span class="p">))()]</span> <span class="c1"># INVALID</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>A further exception applies when an assignment expression occurs in a
|
|||
|
comprehension whose containing scope is a class scope. If the rules
|
|||
|
above were to result in the target being assigned in that class’s
|
|||
|
scope, the assignment expression is expressly invalid. This case also raises
|
|||
|
<code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code>:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Example</span><span class="p">:</span>
|
|||
|
<span class="p">[(</span><span class="n">j</span> <span class="o">:=</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span> <span class="c1"># INVALID</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>(The reason for the latter exception is the implicit function scope created
|
|||
|
for comprehensions – there is currently no runtime mechanism for a
|
|||
|
function to refer to a variable in the containing class scope, and we
|
|||
|
do not want to add such a mechanism. If this issue ever gets resolved
|
|||
|
this special case may be removed from the specification of assignment
|
|||
|
expressions. Note that the problem already exists for <em>using</em> a
|
|||
|
variable defined in the class scope from a comprehension.)</p>
|
|||
|
<p>See Appendix B for some examples of how the rules for targets in
|
|||
|
comprehensions translate to equivalent code.</p>
|
|||
|
</section>
|
|||
|
<section id="relative-precedence-of">
|
|||
|
<h3><a class="toc-backref" href="#relative-precedence-of" role="doc-backlink">Relative precedence of <code class="docutils literal notranslate"><span class="pre">:=</span></code></a></h3>
|
|||
|
<p>The <code class="docutils literal notranslate"><span class="pre">:=</span></code> operator groups more tightly than a comma in all syntactic
|
|||
|
positions where it is legal, but less tightly than all other operators,
|
|||
|
including <code class="docutils literal notranslate"><span class="pre">or</span></code>, <code class="docutils literal notranslate"><span class="pre">and</span></code>, <code class="docutils literal notranslate"><span class="pre">not</span></code>, and conditional expressions
|
|||
|
(<code class="docutils literal notranslate"><span class="pre">A</span> <span class="pre">if</span> <span class="pre">C</span> <span class="pre">else</span> <span class="pre">B</span></code>). As follows from section
|
|||
|
“Exceptional cases” above, it is never allowed at the same level as
|
|||
|
<code class="docutils literal notranslate"><span class="pre">=</span></code>. In case a different grouping is desired, parentheses should be
|
|||
|
used.</p>
|
|||
|
<p>The <code class="docutils literal notranslate"><span class="pre">:=</span></code> operator may be used directly in a positional function call
|
|||
|
argument; however it is invalid directly in a keyword argument.</p>
|
|||
|
<p>Some examples to clarify what’s technically valid or invalid:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># INVALID</span>
|
|||
|
<span class="n">x</span> <span class="o">:=</span> <span class="mi">0</span>
|
|||
|
|
|||
|
<span class="c1"># Valid alternative</span>
|
|||
|
<span class="p">(</span><span class="n">x</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="c1"># INVALID</span>
|
|||
|
<span class="n">x</span> <span class="o">=</span> <span class="n">y</span> <span class="o">:=</span> <span class="mi">0</span>
|
|||
|
|
|||
|
<span class="c1"># Valid alternative</span>
|
|||
|
<span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">y</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="c1"># Valid</span>
|
|||
|
<span class="nb">len</span><span class="p">(</span><span class="n">lines</span> <span class="o">:=</span> <span class="n">f</span><span class="o">.</span><span class="n">readlines</span><span class="p">())</span>
|
|||
|
|
|||
|
<span class="c1"># Valid</span>
|
|||
|
<span class="n">foo</span><span class="p">(</span><span class="n">x</span> <span class="o">:=</span> <span class="mi">3</span><span class="p">,</span> <span class="n">cat</span><span class="o">=</span><span class="s1">'vector'</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="c1"># INVALID</span>
|
|||
|
<span class="n">foo</span><span class="p">(</span><span class="n">cat</span><span class="o">=</span><span class="n">category</span> <span class="o">:=</span> <span class="s1">'vector'</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="c1"># Valid alternative</span>
|
|||
|
<span class="n">foo</span><span class="p">(</span><span class="n">cat</span><span class="o">=</span><span class="p">(</span><span class="n">category</span> <span class="o">:=</span> <span class="s1">'vector'</span><span class="p">))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Most of the “valid” examples above are not recommended, since human
|
|||
|
readers of Python source code who are quickly glancing at some code
|
|||
|
may miss the distinction. But simple cases are not objectionable:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Valid</span>
|
|||
|
<span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">longline</span> <span class="o">:=</span> <span class="n">line</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">100</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"Extremely long line:"</span><span class="p">,</span> <span class="n">longline</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This PEP recommends always putting spaces around <code class="docutils literal notranslate"><span class="pre">:=</span></code>, similar to
|
|||
|
<a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a>’s recommendation for <code class="docutils literal notranslate"><span class="pre">=</span></code> when used for assignment, whereas the
|
|||
|
latter disallows spaces around <code class="docutils literal notranslate"><span class="pre">=</span></code> used for keyword arguments.)</p>
|
|||
|
</section>
|
|||
|
<section id="change-to-evaluation-order">
|
|||
|
<h3><a class="toc-backref" href="#change-to-evaluation-order" role="doc-backlink">Change to evaluation order</a></h3>
|
|||
|
<p>In order to have precisely defined semantics, the proposal requires
|
|||
|
evaluation order to be well-defined. This is technically not a new
|
|||
|
requirement, as function calls may already have side effects. Python
|
|||
|
already has a rule that subexpressions are generally evaluated from
|
|||
|
left to right. However, assignment expressions make these side
|
|||
|
effects more visible, and we propose a single change to the current
|
|||
|
evaluation order:</p>
|
|||
|
<ul class="simple">
|
|||
|
<li>In a dict comprehension <code class="docutils literal notranslate"><span class="pre">{X:</span> <span class="pre">Y</span> <span class="pre">for</span> <span class="pre">...}</span></code>, <code class="docutils literal notranslate"><span class="pre">Y</span></code> is currently
|
|||
|
evaluated before <code class="docutils literal notranslate"><span class="pre">X</span></code>. We propose to change this so that <code class="docutils literal notranslate"><span class="pre">X</span></code> is
|
|||
|
evaluated before <code class="docutils literal notranslate"><span class="pre">Y</span></code>. (In a dict display like <code class="docutils literal notranslate"><span class="pre">{X:</span> <span class="pre">Y}</span></code> this is
|
|||
|
already the case, and also in <code class="docutils literal notranslate"><span class="pre">dict((X,</span> <span class="pre">Y)</span> <span class="pre">for</span> <span class="pre">...)</span></code> which should
|
|||
|
clearly be equivalent to the dict comprehension.)</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="differences-between-assignment-expressions-and-assignment-statements">
|
|||
|
<h3><a class="toc-backref" href="#differences-between-assignment-expressions-and-assignment-statements" role="doc-backlink">Differences between assignment expressions and assignment statements</a></h3>
|
|||
|
<p>Most importantly, since <code class="docutils literal notranslate"><span class="pre">:=</span></code> is an expression, it can be used in contexts
|
|||
|
where statements are illegal, including lambda functions and comprehensions.</p>
|
|||
|
<p>Conversely, assignment expressions don’t support the advanced features
|
|||
|
found in assignment statements:</p>
|
|||
|
<ul>
|
|||
|
<li>Multiple targets are not directly supported:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span> <span class="o">=</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># Equivalent: (z := (y := (x := 0)))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Single assignment targets other than a single <code class="docutils literal notranslate"><span class="pre">NAME</span></code> are
|
|||
|
not supported:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># No equivalent</span>
|
|||
|
<span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">rest</span> <span class="o">=</span> <span class="p">[]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Priority around commas is different:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span> <span class="c1"># Sets x to (1, 2)</span>
|
|||
|
<span class="p">(</span><span class="n">x</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1"># Sets x to 1</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Iterable packing and unpacking (both regular or extended forms) are
|
|||
|
not supported:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Equivalent needs extra parentheses</span>
|
|||
|
<span class="n">loc</span> <span class="o">=</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="c1"># Use (loc := (x, y))</span>
|
|||
|
<span class="n">info</span> <span class="o">=</span> <span class="n">name</span><span class="p">,</span> <span class="n">phone</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span> <span class="c1"># Use (info := (name, phone, *rest))</span>
|
|||
|
|
|||
|
<span class="c1"># No equivalent</span>
|
|||
|
<span class="n">px</span><span class="p">,</span> <span class="n">py</span><span class="p">,</span> <span class="n">pz</span> <span class="o">=</span> <span class="n">position</span>
|
|||
|
<span class="n">name</span><span class="p">,</span> <span class="n">phone</span><span class="p">,</span> <span class="n">email</span><span class="p">,</span> <span class="o">*</span><span class="n">other_info</span> <span class="o">=</span> <span class="n">contact</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Inline type annotations are not supported:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Closest equivalent is "p: Optional[int]" as a separate declaration</span>
|
|||
|
<span class="n">p</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Augmented assignment is not supported:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">total</span> <span class="o">+=</span> <span class="n">tax</span> <span class="c1"># Equivalent: (total := total + tax)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="specification-changes-during-implementation">
|
|||
|
<h2><a class="toc-backref" href="#specification-changes-during-implementation" role="doc-backlink">Specification changes during implementation</a></h2>
|
|||
|
<p>The following changes have been made based on implementation experience and
|
|||
|
additional review after the PEP was first accepted and before Python 3.8 was
|
|||
|
released:</p>
|
|||
|
<ul class="simple">
|
|||
|
<li>for consistency with other similar exceptions, and to avoid locking in an
|
|||
|
exception name that is not necessarily going to improve clarity for end users,
|
|||
|
the originally proposed <code class="docutils literal notranslate"><span class="pre">TargetScopeError</span></code> subclass of <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> was
|
|||
|
dropped in favour of just raising <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> directly. <a class="footnote-reference brackets" href="#id6" id="id2">[3]</a></li>
|
|||
|
<li>due to a limitation in CPython’s symbol table analysis process, the reference
|
|||
|
implementation raises <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> for all uses of named expressions inside
|
|||
|
comprehension iterable expressions, rather than only raising them when the
|
|||
|
named expression target conflicts with one of the iteration variables in the
|
|||
|
comprehension. This could be revisited given sufficiently compelling examples,
|
|||
|
but the extra complexity needed to implement the more selective restriction
|
|||
|
doesn’t seem worthwhile for purely hypothetical use cases.</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="examples">
|
|||
|
<h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2>
|
|||
|
<section id="examples-from-the-python-standard-library">
|
|||
|
<h3><a class="toc-backref" href="#examples-from-the-python-standard-library" role="doc-backlink">Examples from the Python standard library</a></h3>
|
|||
|
<section id="site-py">
|
|||
|
<h4><a class="toc-backref" href="#site-py" role="doc-backlink">site.py</a></h4>
|
|||
|
<p><em>env_base</em> is only used on these lines, putting its assignment on the if
|
|||
|
moves it as the “header” of the block.</p>
|
|||
|
<ul>
|
|||
|
<li>Current:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">env_base</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"PYTHONUSERBASE"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">env_base</span><span class="p">:</span>
|
|||
|
<span class="k">return</span> <span class="n">env_base</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Improved:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">env_base</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"PYTHONUSERBASE"</span><span class="p">,</span> <span class="kc">None</span><span class="p">):</span>
|
|||
|
<span class="k">return</span> <span class="n">env_base</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="pydecimal-py">
|
|||
|
<h4><a class="toc-backref" href="#pydecimal-py" role="doc-backlink">_pydecimal.py</a></h4>
|
|||
|
<p>Avoid nested <code class="docutils literal notranslate"><span class="pre">if</span></code> and remove one indentation level.</p>
|
|||
|
<ul>
|
|||
|
<li>Current:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_special</span><span class="p">:</span>
|
|||
|
<span class="n">ans</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_check_nans</span><span class="p">(</span><span class="n">context</span><span class="o">=</span><span class="n">context</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">ans</span><span class="p">:</span>
|
|||
|
<span class="k">return</span> <span class="n">ans</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Improved:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_special</span> <span class="ow">and</span> <span class="p">(</span><span class="n">ans</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_check_nans</span><span class="p">(</span><span class="n">context</span><span class="o">=</span><span class="n">context</span><span class="p">)):</span>
|
|||
|
<span class="k">return</span> <span class="n">ans</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="copy-py">
|
|||
|
<h4><a class="toc-backref" href="#copy-py" role="doc-backlink">copy.py</a></h4>
|
|||
|
<p>Code looks more regular and avoid multiple nested if.
|
|||
|
(See Appendix A for the origin of this example.)</p>
|
|||
|
<ul>
|
|||
|
<li>Current:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">reductor</span> <span class="o">=</span> <span class="n">dispatch_table</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">reductor</span><span class="p">:</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">reductor</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce_ex__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">reductor</span><span class="p">:</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">reductor</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">reductor</span><span class="p">:</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">()</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="n">Error</span><span class="p">(</span>
|
|||
|
<span class="s2">"un(deep)copyable object of type </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="bp">cls</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Improved:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">reductor</span> <span class="o">:=</span> <span class="n">dispatch_table</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
|||
|
<span class="k">elif</span> <span class="n">reductor</span> <span class="o">:=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce_ex__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">):</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
|
|||
|
<span class="k">elif</span> <span class="n">reductor</span> <span class="o">:=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">):</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">()</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="n">Error</span><span class="p">(</span><span class="s2">"un(deep)copyable object of type </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="bp">cls</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="datetime-py">
|
|||
|
<h4><a class="toc-backref" href="#datetime-py" role="doc-backlink">datetime.py</a></h4>
|
|||
|
<p><em>tz</em> is only used for <code class="docutils literal notranslate"><span class="pre">s</span> <span class="pre">+=</span> <span class="pre">tz</span></code>, moving its assignment inside the if
|
|||
|
helps to show its scope.</p>
|
|||
|
<ul>
|
|||
|
<li>Current:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">s</span> <span class="o">=</span> <span class="n">_format_time</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_hour</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_minute</span><span class="p">,</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_second</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_microsecond</span><span class="p">,</span>
|
|||
|
<span class="n">timespec</span><span class="p">)</span>
|
|||
|
<span class="n">tz</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_tzstr</span><span class="p">()</span>
|
|||
|
<span class="k">if</span> <span class="n">tz</span><span class="p">:</span>
|
|||
|
<span class="n">s</span> <span class="o">+=</span> <span class="n">tz</span>
|
|||
|
<span class="k">return</span> <span class="n">s</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Improved:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">s</span> <span class="o">=</span> <span class="n">_format_time</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_hour</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_minute</span><span class="p">,</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_second</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_microsecond</span><span class="p">,</span>
|
|||
|
<span class="n">timespec</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">tz</span> <span class="o">:=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_tzstr</span><span class="p">():</span>
|
|||
|
<span class="n">s</span> <span class="o">+=</span> <span class="n">tz</span>
|
|||
|
<span class="k">return</span> <span class="n">s</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="sysconfig-py">
|
|||
|
<h4><a class="toc-backref" href="#sysconfig-py" role="doc-backlink">sysconfig.py</a></h4>
|
|||
|
<p>Calling <code class="docutils literal notranslate"><span class="pre">fp.readline()</span></code> in the <code class="docutils literal notranslate"><span class="pre">while</span></code> condition and calling
|
|||
|
<code class="docutils literal notranslate"><span class="pre">.match()</span></code> on the if lines make the code more compact without making
|
|||
|
it harder to understand.</p>
|
|||
|
<ul>
|
|||
|
<li>Current:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
|
|||
|
<span class="n">line</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span>
|
|||
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">line</span><span class="p">:</span>
|
|||
|
<span class="k">break</span>
|
|||
|
<span class="n">m</span> <span class="o">=</span> <span class="n">define_rx</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">m</span><span class="p">:</span>
|
|||
|
<span class="n">n</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
|
|||
|
<span class="k">try</span><span class="p">:</span>
|
|||
|
<span class="n">v</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
|
|||
|
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
|||
|
<span class="k">pass</span>
|
|||
|
<span class="nb">vars</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">m</span> <span class="o">=</span> <span class="n">undef_rx</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">m</span><span class="p">:</span>
|
|||
|
<span class="nb">vars</span><span class="p">[</span><span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Improved:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">while</span> <span class="n">line</span> <span class="o">:=</span> <span class="n">fp</span><span class="o">.</span><span class="n">readline</span><span class="p">():</span>
|
|||
|
<span class="k">if</span> <span class="n">m</span> <span class="o">:=</span> <span class="n">define_rx</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
|
|||
|
<span class="n">n</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
|
|||
|
<span class="k">try</span><span class="p">:</span>
|
|||
|
<span class="n">v</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
|
|||
|
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
|
|||
|
<span class="k">pass</span>
|
|||
|
<span class="nb">vars</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
|
|||
|
<span class="k">elif</span> <span class="n">m</span> <span class="o">:=</span> <span class="n">undef_rx</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
|
|||
|
<span class="nb">vars</span><span class="p">[</span><span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="simplifying-list-comprehensions">
|
|||
|
<h3><a class="toc-backref" href="#simplifying-list-comprehensions" role="doc-backlink">Simplifying list comprehensions</a></h3>
|
|||
|
<p>A list comprehension can map and filter efficiently by capturing
|
|||
|
the condition:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">results</span> <span class="o">=</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">x</span><span class="o">/</span><span class="n">y</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">input_data</span> <span class="k">if</span> <span class="p">(</span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="o">></span> <span class="mi">0</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Similarly, a subexpression can be reused within the main expression, by
|
|||
|
giving it a name on first use:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[[</span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Note that in both cases the variable <code class="docutils literal notranslate"><span class="pre">y</span></code> is bound in the containing
|
|||
|
scope (i.e. at the same level as <code class="docutils literal notranslate"><span class="pre">results</span></code> or <code class="docutils literal notranslate"><span class="pre">stuff</span></code>).</p>
|
|||
|
</section>
|
|||
|
<section id="capturing-condition-values">
|
|||
|
<h3><a class="toc-backref" href="#capturing-condition-values" role="doc-backlink">Capturing condition values</a></h3>
|
|||
|
<p>Assignment expressions can be used to good effect in the header of
|
|||
|
an <code class="docutils literal notranslate"><span class="pre">if</span></code> or <code class="docutils literal notranslate"><span class="pre">while</span></code> statement:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Loop-and-a-half</span>
|
|||
|
<span class="k">while</span> <span class="p">(</span><span class="n">command</span> <span class="o">:=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"> "</span><span class="p">))</span> <span class="o">!=</span> <span class="s2">"quit"</span><span class="p">:</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"You entered:"</span><span class="p">,</span> <span class="n">command</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="c1"># Capturing regular expression match objects</span>
|
|||
|
<span class="c1"># See, for instance, Lib/pydoc.py, which uses a multiline spelling</span>
|
|||
|
<span class="c1"># of this effect</span>
|
|||
|
<span class="k">if</span> <span class="n">match</span> <span class="o">:=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pat</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"Found:"</span><span class="p">,</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
|
|||
|
<span class="c1"># The same syntax chains nicely into 'elif' statements, unlike the</span>
|
|||
|
<span class="c1"># equivalent using assignment statements.</span>
|
|||
|
<span class="k">elif</span> <span class="n">match</span> <span class="o">:=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">otherpat</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"Alternate found:"</span><span class="p">,</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
|
|||
|
<span class="k">elif</span> <span class="n">match</span> <span class="o">:=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">third</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"Fallback found:"</span><span class="p">,</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
|
|||
|
|
|||
|
<span class="c1"># Reading socket data until an empty string is returned</span>
|
|||
|
<span class="k">while</span> <span class="n">data</span> <span class="o">:=</span> <span class="n">sock</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">8192</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"Received data:"</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Particularly with the <code class="docutils literal notranslate"><span class="pre">while</span></code> loop, this can remove the need to have an
|
|||
|
infinite loop, an assignment, and a condition. It also creates a smooth
|
|||
|
parallel between a loop which simply uses a function call as its condition,
|
|||
|
and one which uses that as its condition but also uses the actual value.</p>
|
|||
|
</section>
|
|||
|
<section id="fork">
|
|||
|
<h3><a class="toc-backref" href="#fork" role="doc-backlink">Fork</a></h3>
|
|||
|
<p>An example from the low-level UNIX world:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">pid</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">fork</span><span class="p">():</span>
|
|||
|
<span class="c1"># Parent code</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="c1"># Child code</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="rejected-alternative-proposals">
|
|||
|
<h2><a class="toc-backref" href="#rejected-alternative-proposals" role="doc-backlink">Rejected alternative proposals</a></h2>
|
|||
|
<p>Proposals broadly similar to this one have come up frequently on python-ideas.
|
|||
|
Below are a number of alternative syntaxes, some of them specific to
|
|||
|
comprehensions, which have been rejected in favour of the one given above.</p>
|
|||
|
<section id="changing-the-scope-rules-for-comprehensions">
|
|||
|
<h3><a class="toc-backref" href="#changing-the-scope-rules-for-comprehensions" role="doc-backlink">Changing the scope rules for comprehensions</a></h3>
|
|||
|
<p>A previous version of this PEP proposed subtle changes to the scope
|
|||
|
rules for comprehensions, to make them more usable in class scope and
|
|||
|
to unify the scope of the “outermost iterable” and the rest of the
|
|||
|
comprehension. However, this part of the proposal would have caused
|
|||
|
backwards incompatibilities, and has been withdrawn so the PEP can
|
|||
|
focus on assignment expressions.</p>
|
|||
|
</section>
|
|||
|
<section id="alternative-spellings">
|
|||
|
<h3><a class="toc-backref" href="#alternative-spellings" role="doc-backlink">Alternative spellings</a></h3>
|
|||
|
<p>Broadly the same semantics as the current proposal, but spelled differently.</p>
|
|||
|
<ol class="arabic">
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">EXPR</span> <span class="pre">as</span> <span class="pre">NAME</span></code>:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">as</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Since <code class="docutils literal notranslate"><span class="pre">EXPR</span> <span class="pre">as</span> <span class="pre">NAME</span></code> already has meaning in <code class="docutils literal notranslate"><span class="pre">import</span></code>,
|
|||
|
<code class="docutils literal notranslate"><span class="pre">except</span></code> and <code class="docutils literal notranslate"><span class="pre">with</span></code> statements (with different semantics), this
|
|||
|
would create unnecessary confusion or require special-casing
|
|||
|
(e.g. to forbid assignment within the headers of these statements).</p>
|
|||
|
<p>(Note that <code class="docutils literal notranslate"><span class="pre">with</span> <span class="pre">EXPR</span> <span class="pre">as</span> <span class="pre">VAR</span></code> does <em>not</em> simply assign the value
|
|||
|
of <code class="docutils literal notranslate"><span class="pre">EXPR</span></code> to <code class="docutils literal notranslate"><span class="pre">VAR</span></code> – it calls <code class="docutils literal notranslate"><span class="pre">EXPR.__enter__()</span></code> and assigns
|
|||
|
the result of <em>that</em> to <code class="docutils literal notranslate"><span class="pre">VAR</span></code>.)</p>
|
|||
|
<p>Additional reasons to prefer <code class="docutils literal notranslate"><span class="pre">:=</span></code> over this spelling include:</p>
|
|||
|
<ul>
|
|||
|
<li>In <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">f(x)</span> <span class="pre">as</span> <span class="pre">y</span></code> the assignment target doesn’t jump out at you
|
|||
|
– it just reads like <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">f</span> <span class="pre">x</span> <span class="pre">blah</span> <span class="pre">blah</span></code> and it is too similar
|
|||
|
visually to <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">f(x)</span> <span class="pre">and</span> <span class="pre">y</span></code>.</li>
|
|||
|
<li>In all other situations where an <code class="docutils literal notranslate"><span class="pre">as</span></code> clause is allowed, even
|
|||
|
readers with intermediary skills are led to anticipate that
|
|||
|
clause (however optional) by the keyword that starts the line,
|
|||
|
and the grammar ties that keyword closely to the as clause:<ul class="simple">
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">foo</span> <span class="pre">as</span> <span class="pre">bar</span></code></li>
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">except</span> <span class="pre">Exc</span> <span class="pre">as</span> <span class="pre">var</span></code></li>
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">with</span> <span class="pre">ctxmgr()</span> <span class="pre">as</span> <span class="pre">var</span></code></li>
|
|||
|
</ul>
|
|||
|
<p>To the contrary, the assignment expression does not belong to the
|
|||
|
<code class="docutils literal notranslate"><span class="pre">if</span></code> or <code class="docutils literal notranslate"><span class="pre">while</span></code> that starts the line, and we intentionally
|
|||
|
allow assignment expressions in other contexts as well.</p>
|
|||
|
</li>
|
|||
|
<li>The parallel cadence between<ul class="simple">
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">NAME</span> <span class="pre">=</span> <span class="pre">EXPR</span></code></li>
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">NAME</span> <span class="pre">:=</span> <span class="pre">EXPR</span></code></li>
|
|||
|
</ul>
|
|||
|
<p>reinforces the visual recognition of assignment expressions.</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">EXPR</span> <span class="pre">-></span> <span class="pre">NAME</span></code>:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">-></span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This syntax is inspired by languages such as R and Haskell, and some
|
|||
|
programmable calculators. (Note that a left-facing arrow <code class="docutils literal notranslate"><span class="pre">y</span> <span class="pre"><-</span> <span class="pre">f(x)</span></code> is
|
|||
|
not possible in Python, as it would be interpreted as less-than and unary
|
|||
|
minus.) This syntax has a slight advantage over ‘as’ in that it does not
|
|||
|
conflict with <code class="docutils literal notranslate"><span class="pre">with</span></code>, <code class="docutils literal notranslate"><span class="pre">except</span></code> and <code class="docutils literal notranslate"><span class="pre">import</span></code>, but otherwise is
|
|||
|
equivalent. But it is entirely unrelated to Python’s other use of
|
|||
|
<code class="docutils literal notranslate"><span class="pre">-></span></code> (function return type annotations), and compared to <code class="docutils literal notranslate"><span class="pre">:=</span></code>
|
|||
|
(which dates back to Algol-58) it has a much weaker tradition.</p>
|
|||
|
</li>
|
|||
|
<li>Adorning statement-local names with a leading dot:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[[(</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">as</span> <span class="o">.</span><span class="n">y</span><span class="p">),</span> <span class="n">x</span><span class="o">/.</span><span class="n">y</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span> <span class="c1"># with "as"</span>
|
|||
|
<span class="n">stuff</span> <span class="o">=</span> <span class="p">[[(</span><span class="o">.</span><span class="n">y</span> <span class="o">:=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)),</span> <span class="n">x</span><span class="o">/.</span><span class="n">y</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span> <span class="c1"># with ":="</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This has the advantage that leaked usage can be readily detected, removing
|
|||
|
some forms of syntactic ambiguity. However, this would be the only place
|
|||
|
in Python where a variable’s scope is encoded into its name, making
|
|||
|
refactoring harder.</p>
|
|||
|
</li>
|
|||
|
<li>Adding a <code class="docutils literal notranslate"><span class="pre">where:</span></code> to any statement to create local name bindings:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="n">where</span><span class="p">:</span>
|
|||
|
<span class="n">x</span> <span class="o">=</span> <span class="n">spam</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="n">q</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Execution order is inverted (the indented body is performed first, followed
|
|||
|
by the “header”). This requires a new keyword, unless an existing keyword
|
|||
|
is repurposed (most likely <code class="docutils literal notranslate"><span class="pre">with:</span></code>). See <a class="pep reference internal" href="../pep-3150/" title="PEP 3150 – Statement local namespaces (aka “given” clause)">PEP 3150</a> for prior discussion
|
|||
|
on this subject (with the proposed keyword being <code class="docutils literal notranslate"><span class="pre">given:</span></code>).</p>
|
|||
|
</li>
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">TARGET</span> <span class="pre">from</span> <span class="pre">EXPR</span></code>:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[[</span><span class="n">y</span> <span class="kn">from</span><span class="w"> </span><span class="nn">f</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This syntax has fewer conflicts than <code class="docutils literal notranslate"><span class="pre">as</span></code> does (conflicting only with the
|
|||
|
<code class="docutils literal notranslate"><span class="pre">raise</span> <span class="pre">Exc</span> <span class="pre">from</span> <span class="pre">Exc</span></code> notation), but is otherwise comparable to it. Instead
|
|||
|
of paralleling <code class="docutils literal notranslate"><span class="pre">with</span> <span class="pre">expr</span> <span class="pre">as</span> <span class="pre">target:</span></code> (which can be useful but can also be
|
|||
|
confusing), this has no parallels, but is evocative.</p>
|
|||
|
</li>
|
|||
|
</ol>
|
|||
|
</section>
|
|||
|
<section id="special-casing-conditional-statements">
|
|||
|
<h3><a class="toc-backref" href="#special-casing-conditional-statements" role="doc-backlink">Special-casing conditional statements</a></h3>
|
|||
|
<p>One of the most popular use-cases is <code class="docutils literal notranslate"><span class="pre">if</span></code> and <code class="docutils literal notranslate"><span class="pre">while</span></code> statements. Instead
|
|||
|
of a more general solution, this proposal enhances the syntax of these two
|
|||
|
statements to add a means of capturing the compared value:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">pat</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span> <span class="k">as</span> <span class="n">match</span><span class="p">:</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"Found:"</span><span class="p">,</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This works beautifully if and ONLY if the desired condition is based on the
|
|||
|
truthiness of the captured value. It is thus effective for specific
|
|||
|
use-cases (regex matches, socket reads that return <code class="docutils literal notranslate"><span class="pre">''</span></code> when done), and
|
|||
|
completely useless in more complicated cases (e.g. where the condition is
|
|||
|
<code class="docutils literal notranslate"><span class="pre">f(x)</span> <span class="pre"><</span> <span class="pre">0</span></code> and you want to capture the value of <code class="docutils literal notranslate"><span class="pre">f(x)</span></code>). It also has
|
|||
|
no benefit to list comprehensions.</p>
|
|||
|
<p>Advantages: No syntactic ambiguities. Disadvantages: Answers only a fraction
|
|||
|
of possible use-cases, even in <code class="docutils literal notranslate"><span class="pre">if</span></code>/<code class="docutils literal notranslate"><span class="pre">while</span></code> statements.</p>
|
|||
|
</section>
|
|||
|
<section id="special-casing-comprehensions">
|
|||
|
<h3><a class="toc-backref" href="#special-casing-comprehensions" role="doc-backlink">Special-casing comprehensions</a></h3>
|
|||
|
<p>Another common use-case is comprehensions (list/set/dict, and genexps). As
|
|||
|
above, proposals have been made for comprehension-specific solutions.</p>
|
|||
|
<ol class="arabic">
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">where</span></code>, <code class="docutils literal notranslate"><span class="pre">let</span></code>, or <code class="docutils literal notranslate"><span class="pre">given</span></code>:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">)</span> <span class="n">where</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
<span class="n">stuff</span> <span class="o">=</span> <span class="p">[(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">)</span> <span class="n">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
<span class="n">stuff</span> <span class="o">=</span> <span class="p">[(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">)</span> <span class="n">given</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This brings the subexpression to a location in between the ‘for’ loop and
|
|||
|
the expression. It introduces an additional language keyword, which creates
|
|||
|
conflicts. Of the three, <code class="docutils literal notranslate"><span class="pre">where</span></code> reads the most cleanly, but also has the
|
|||
|
greatest potential for conflict (e.g. SQLAlchemy and numpy have <code class="docutils literal notranslate"><span class="pre">where</span></code>
|
|||
|
methods, as does <code class="docutils literal notranslate"><span class="pre">tkinter.dnd.Icon</span></code> in the standard library).</p>
|
|||
|
</li>
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">with</span> <span class="pre">NAME</span> <span class="pre">=</span> <span class="pre">EXPR</span></code>:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">)</span> <span class="k">with</span> <span class="n">y</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>As above, but reusing the <code class="docutils literal notranslate"><span class="pre">with</span></code> keyword. Doesn’t read too badly, and needs
|
|||
|
no additional language keyword. Is restricted to comprehensions, though,
|
|||
|
and cannot as easily be transformed into “longhand” for-loop syntax. Has
|
|||
|
the C problem that an equals sign in an expression can now create a name
|
|||
|
binding, rather than performing a comparison. Would raise the question of
|
|||
|
why “with NAME = EXPR:” cannot be used as a statement on its own.</p>
|
|||
|
</li>
|
|||
|
<li><code class="docutils literal notranslate"><span class="pre">with</span> <span class="pre">EXPR</span> <span class="pre">as</span> <span class="pre">NAME</span></code>:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="o">/</span><span class="n">y</span><span class="p">)</span> <span class="k">with</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">as</span> <span class="n">y</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>As per option 2, but using <code class="docutils literal notranslate"><span class="pre">as</span></code> rather than an equals sign. Aligns
|
|||
|
syntactically with other uses of <code class="docutils literal notranslate"><span class="pre">as</span></code> for name binding, but a simple
|
|||
|
transformation to for-loop longhand would create drastically different
|
|||
|
semantics; the meaning of <code class="docutils literal notranslate"><span class="pre">with</span></code> inside a comprehension would be
|
|||
|
completely different from the meaning as a stand-alone statement, while
|
|||
|
retaining identical syntax.</p>
|
|||
|
</li>
|
|||
|
</ol>
|
|||
|
<p>Regardless of the spelling chosen, this introduces a stark difference between
|
|||
|
comprehensions and the equivalent unrolled long-hand form of the loop. It is
|
|||
|
no longer possible to unwrap the loop into statement form without reworking
|
|||
|
any name bindings. The only keyword that can be repurposed to this task is
|
|||
|
<code class="docutils literal notranslate"><span class="pre">with</span></code>, thus giving it sneakily different semantics in a comprehension than
|
|||
|
in a statement; alternatively, a new keyword is needed, with all the costs
|
|||
|
therein.</p>
|
|||
|
</section>
|
|||
|
<section id="lowering-operator-precedence">
|
|||
|
<h3><a class="toc-backref" href="#lowering-operator-precedence" role="doc-backlink">Lowering operator precedence</a></h3>
|
|||
|
<p>There are two logical precedences for the <code class="docutils literal notranslate"><span class="pre">:=</span></code> operator. Either it should
|
|||
|
bind as loosely as possible, as does statement-assignment; or it should bind
|
|||
|
more tightly than comparison operators. Placing its precedence between the
|
|||
|
comparison and arithmetic operators (to be precise: just lower than bitwise
|
|||
|
OR) allows most uses inside <code class="docutils literal notranslate"><span class="pre">while</span></code> and <code class="docutils literal notranslate"><span class="pre">if</span></code> conditions to be spelled
|
|||
|
without parentheses, as it is most likely that you wish to capture the value
|
|||
|
of something, then perform a comparison on it:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pos</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
|
|||
|
<span class="k">while</span> <span class="n">pos</span> <span class="o">:=</span> <span class="n">buffer</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">search_term</span><span class="p">,</span> <span class="n">pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">0</span><span class="p">:</span>
|
|||
|
<span class="o">...</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Once find() returns -1, the loop terminates. If <code class="docutils literal notranslate"><span class="pre">:=</span></code> binds as loosely as
|
|||
|
<code class="docutils literal notranslate"><span class="pre">=</span></code> does, this would capture the result of the comparison (generally either
|
|||
|
<code class="docutils literal notranslate"><span class="pre">True</span></code> or <code class="docutils literal notranslate"><span class="pre">False</span></code>), which is less useful.</p>
|
|||
|
<p>While this behaviour would be convenient in many situations, it is also harder
|
|||
|
to explain than “the := operator behaves just like the assignment statement”,
|
|||
|
and as such, the precedence for <code class="docutils literal notranslate"><span class="pre">:=</span></code> has been made as close as possible to
|
|||
|
that of <code class="docutils literal notranslate"><span class="pre">=</span></code> (with the exception that it binds tighter than comma).</p>
|
|||
|
</section>
|
|||
|
<section id="allowing-commas-to-the-right">
|
|||
|
<h3><a class="toc-backref" href="#allowing-commas-to-the-right" role="doc-backlink">Allowing commas to the right</a></h3>
|
|||
|
<p>Some critics have claimed that the assignment expressions should allow
|
|||
|
unparenthesized tuples on the right, so that these two would be equivalent:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">point</span> <span class="o">:=</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="p">(</span><span class="n">point</span> <span class="o">:=</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>(With the current version of the proposal, the latter would be
|
|||
|
equivalent to <code class="docutils literal notranslate"><span class="pre">((point</span> <span class="pre">:=</span> <span class="pre">x),</span> <span class="pre">y)</span></code>.)</p>
|
|||
|
<p>However, adopting this stance would logically lead to the conclusion
|
|||
|
that when used in a function call, assignment expressions also bind
|
|||
|
less tight than comma, so we’d have the following confusing equivalence:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">foo</span><span class="p">(</span><span class="n">x</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
|
|||
|
<span class="n">foo</span><span class="p">(</span><span class="n">x</span> <span class="o">:=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The less confusing option is to make <code class="docutils literal notranslate"><span class="pre">:=</span></code> bind more tightly than comma.</p>
|
|||
|
</section>
|
|||
|
<section id="always-requiring-parentheses">
|
|||
|
<h3><a class="toc-backref" href="#always-requiring-parentheses" role="doc-backlink">Always requiring parentheses</a></h3>
|
|||
|
<p>It’s been proposed to just always require parentheses around an
|
|||
|
assignment expression. This would resolve many ambiguities, and
|
|||
|
indeed parentheses will frequently be needed to extract the desired
|
|||
|
subexpression. But in the following cases the extra parentheses feel
|
|||
|
redundant:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Top level in if</span>
|
|||
|
<span class="k">if</span> <span class="n">match</span> <span class="o">:=</span> <span class="n">pattern</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
|
|||
|
<span class="k">return</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="c1"># Short call</span>
|
|||
|
<span class="nb">len</span><span class="p">(</span><span class="n">lines</span> <span class="o">:=</span> <span class="n">f</span><span class="o">.</span><span class="n">readlines</span><span class="p">())</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="frequently-raised-objections">
|
|||
|
<h2><a class="toc-backref" href="#frequently-raised-objections" role="doc-backlink">Frequently Raised Objections</a></h2>
|
|||
|
<section id="why-not-just-turn-existing-assignment-into-an-expression">
|
|||
|
<h3><a class="toc-backref" href="#why-not-just-turn-existing-assignment-into-an-expression" role="doc-backlink">Why not just turn existing assignment into an expression?</a></h3>
|
|||
|
<p>C and its derivatives define the <code class="docutils literal notranslate"><span class="pre">=</span></code> operator as an expression, rather than
|
|||
|
a statement as is Python’s way. This allows assignments in more contexts,
|
|||
|
including contexts where comparisons are more common. The syntactic similarity
|
|||
|
between <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">(x</span> <span class="pre">==</span> <span class="pre">y)</span></code> and <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">(x</span> <span class="pre">=</span> <span class="pre">y)</span></code> belies their drastically different
|
|||
|
semantics. Thus this proposal uses <code class="docutils literal notranslate"><span class="pre">:=</span></code> to clarify the distinction.</p>
|
|||
|
</section>
|
|||
|
<section id="with-assignment-expressions-why-bother-with-assignment-statements">
|
|||
|
<h3><a class="toc-backref" href="#with-assignment-expressions-why-bother-with-assignment-statements" role="doc-backlink">With assignment expressions, why bother with assignment statements?</a></h3>
|
|||
|
<p>The two forms have different flexibilities. The <code class="docutils literal notranslate"><span class="pre">:=</span></code> operator can be used
|
|||
|
inside a larger expression; the <code class="docutils literal notranslate"><span class="pre">=</span></code> statement can be augmented to <code class="docutils literal notranslate"><span class="pre">+=</span></code> and
|
|||
|
its friends, can be chained, and can assign to attributes and subscripts.</p>
|
|||
|
</section>
|
|||
|
<section id="why-not-use-a-sublocal-scope-and-prevent-namespace-pollution">
|
|||
|
<h3><a class="toc-backref" href="#why-not-use-a-sublocal-scope-and-prevent-namespace-pollution" role="doc-backlink">Why not use a sublocal scope and prevent namespace pollution?</a></h3>
|
|||
|
<p>Previous revisions of this proposal involved sublocal scope (restricted to a
|
|||
|
single statement), preventing name leakage and namespace pollution. While a
|
|||
|
definite advantage in a number of situations, this increases complexity in
|
|||
|
many others, and the costs are not justified by the benefits. In the interests
|
|||
|
of language simplicity, the name bindings created here are exactly equivalent
|
|||
|
to any other name bindings, including that usage at class or module scope will
|
|||
|
create externally-visible names. This is no different from <code class="docutils literal notranslate"><span class="pre">for</span></code> loops or
|
|||
|
other constructs, and can be solved the same way: <code class="docutils literal notranslate"><span class="pre">del</span></code> the name once it is
|
|||
|
no longer needed, or prefix it with an underscore.</p>
|
|||
|
<p>(The author wishes to thank Guido van Rossum and Christoph Groth for their
|
|||
|
suggestions to move the proposal in this direction. <a class="footnote-reference brackets" href="#id5" id="id3">[2]</a>)</p>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="style-guide-recommendations">
|
|||
|
<h2><a class="toc-backref" href="#style-guide-recommendations" role="doc-backlink">Style guide recommendations</a></h2>
|
|||
|
<p>As expression assignments can sometimes be used equivalently to statement
|
|||
|
assignments, the question of which should be preferred will arise. For the
|
|||
|
benefit of style guides such as <a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a>, two recommendations are suggested.</p>
|
|||
|
<ol class="arabic simple">
|
|||
|
<li>If either assignment statements or assignment expressions can be
|
|||
|
used, prefer statements; they are a clear declaration of intent.</li>
|
|||
|
<li>If using assignment expressions would lead to ambiguity about
|
|||
|
execution order, restructure it to use statements instead.</li>
|
|||
|
</ol>
|
|||
|
</section>
|
|||
|
<section id="acknowledgements">
|
|||
|
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
|
|||
|
<p>The authors wish to thank Alyssa Coghlan and Steven D’Aprano for their
|
|||
|
considerable contributions to this proposal, and members of the
|
|||
|
core-mentorship mailing list for assistance with implementation.</p>
|
|||
|
</section>
|
|||
|
<section id="appendix-a-tim-peters-s-findings">
|
|||
|
<h2><a class="toc-backref" href="#appendix-a-tim-peters-s-findings" role="doc-backlink">Appendix A: Tim Peters’s findings</a></h2>
|
|||
|
<p>Here’s a brief essay Tim Peters wrote on the topic.</p>
|
|||
|
<p>I dislike “busy” lines of code, and also dislike putting conceptually
|
|||
|
unrelated logic on a single line. So, for example, instead of:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">i</span> <span class="o">=</span> <span class="n">j</span> <span class="o">=</span> <span class="n">count</span> <span class="o">=</span> <span class="n">nerrors</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>I prefer:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">i</span> <span class="o">=</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
<span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
<span class="n">nerrors</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>instead. So I suspected I’d find few places I’d want to use
|
|||
|
assignment expressions. I didn’t even consider them for lines already
|
|||
|
stretching halfway across the screen. In other cases, “unrelated”
|
|||
|
ruled:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mylast</span> <span class="o">=</span> <span class="n">mylast</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
|||
|
<span class="k">yield</span> <span class="n">mylast</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>is a vast improvement over the briefer:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">yield</span> <span class="p">(</span><span class="n">mylast</span> <span class="o">:=</span> <span class="n">mylast</span><span class="p">[</span><span class="mi">1</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The original two statements are doing entirely different conceptual
|
|||
|
things, and slamming them together is conceptually insane.</p>
|
|||
|
<p>In other cases, combining related logic made it harder to understand,
|
|||
|
such as rewriting:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
|
|||
|
<span class="n">old</span> <span class="o">=</span> <span class="n">total</span>
|
|||
|
<span class="n">total</span> <span class="o">+=</span> <span class="n">term</span>
|
|||
|
<span class="k">if</span> <span class="n">old</span> <span class="o">==</span> <span class="n">total</span><span class="p">:</span>
|
|||
|
<span class="k">return</span> <span class="n">total</span>
|
|||
|
<span class="n">term</span> <span class="o">*=</span> <span class="n">mx2</span> <span class="o">/</span> <span class="p">(</span><span class="n">i</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span>
|
|||
|
<span class="n">i</span> <span class="o">+=</span> <span class="mi">2</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>as the briefer:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">while</span> <span class="n">total</span> <span class="o">!=</span> <span class="p">(</span><span class="n">total</span> <span class="o">:=</span> <span class="n">total</span> <span class="o">+</span> <span class="n">term</span><span class="p">):</span>
|
|||
|
<span class="n">term</span> <span class="o">*=</span> <span class="n">mx2</span> <span class="o">/</span> <span class="p">(</span><span class="n">i</span><span class="o">*</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span>
|
|||
|
<span class="n">i</span> <span class="o">+=</span> <span class="mi">2</span>
|
|||
|
<span class="k">return</span> <span class="n">total</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The <code class="docutils literal notranslate"><span class="pre">while</span></code> test there is too subtle, crucially relying on strict
|
|||
|
left-to-right evaluation in a non-short-circuiting or method-chaining
|
|||
|
context. My brain isn’t wired that way.</p>
|
|||
|
<p>But cases like that were rare. Name binding is very frequent, and
|
|||
|
“sparse is better than dense” does not mean “almost empty is better
|
|||
|
than sparse”. For example, I have many functions that return <code class="docutils literal notranslate"><span class="pre">None</span></code>
|
|||
|
or <code class="docutils literal notranslate"><span class="pre">0</span></code> to communicate “I have nothing useful to return in this case,
|
|||
|
but since that’s expected often I’m not going to annoy you with an
|
|||
|
exception”. This is essentially the same as regular expression search
|
|||
|
functions returning <code class="docutils literal notranslate"><span class="pre">None</span></code> when there is no match. So there was lots
|
|||
|
of code of the form:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">result</span> <span class="o">=</span> <span class="n">solution</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">result</span><span class="p">:</span>
|
|||
|
<span class="c1"># use result</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>I find that clearer, and certainly a bit less typing and
|
|||
|
pattern-matching reading, as:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">result</span> <span class="o">:=</span> <span class="n">solution</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
|
|||
|
<span class="c1"># use result</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>It’s also nice to trade away a small amount of horizontal whitespace
|
|||
|
to get another _line_ of surrounding code on screen. I didn’t give
|
|||
|
much weight to this at first, but it was so very frequent it added up,
|
|||
|
and I soon enough became annoyed that I couldn’t actually run the
|
|||
|
briefer code. That surprised me!</p>
|
|||
|
<p>There are other cases where assignment expressions really shine.
|
|||
|
Rather than pick another from my code, Kirill Balunov gave a lovely
|
|||
|
example from the standard library’s <code class="docutils literal notranslate"><span class="pre">copy()</span></code> function in <code class="docutils literal notranslate"><span class="pre">copy.py</span></code>:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">reductor</span> <span class="o">=</span> <span class="n">dispatch_table</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">reductor</span><span class="p">:</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">reductor</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce_ex__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">reductor</span><span class="p">:</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="n">reductor</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">reductor</span><span class="p">:</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">()</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="n">Error</span><span class="p">(</span><span class="s2">"un(shallow)copyable object of type </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="bp">cls</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The ever-increasing indentation is semantically misleading: the logic
|
|||
|
is conceptually flat, “the first test that succeeds wins”:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">reductor</span> <span class="o">:=</span> <span class="n">dispatch_table</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
|||
|
<span class="k">elif</span> <span class="n">reductor</span> <span class="o">:=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce_ex__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">):</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
|
|||
|
<span class="k">elif</span> <span class="n">reductor</span> <span class="o">:=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s2">"__reduce__"</span><span class="p">,</span> <span class="kc">None</span><span class="p">):</span>
|
|||
|
<span class="n">rv</span> <span class="o">=</span> <span class="n">reductor</span><span class="p">()</span>
|
|||
|
<span class="k">else</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="n">Error</span><span class="p">(</span><span class="s2">"un(shallow)copyable object of type </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="bp">cls</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Using easy assignment expressions allows the visual structure of the
|
|||
|
code to emphasize the conceptual flatness of the logic;
|
|||
|
ever-increasing indentation obscured it.</p>
|
|||
|
<p>A smaller example from my code delighted me, both allowing to put
|
|||
|
inherently related logic in a single line, and allowing to remove an
|
|||
|
annoying “artificial” indentation level:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">diff</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">x_base</span>
|
|||
|
<span class="k">if</span> <span class="n">diff</span><span class="p">:</span>
|
|||
|
<span class="n">g</span> <span class="o">=</span> <span class="n">gcd</span><span class="p">(</span><span class="n">diff</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">g</span> <span class="o">></span> <span class="mi">1</span><span class="p">:</span>
|
|||
|
<span class="k">return</span> <span class="n">g</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>became:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="p">(</span><span class="n">diff</span> <span class="o">:=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">x_base</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">g</span> <span class="o">:=</span> <span class="n">gcd</span><span class="p">(</span><span class="n">diff</span><span class="p">,</span> <span class="n">n</span><span class="p">))</span> <span class="o">></span> <span class="mi">1</span><span class="p">:</span>
|
|||
|
<span class="k">return</span> <span class="n">g</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>That <code class="docutils literal notranslate"><span class="pre">if</span></code> is about as long as I want my lines to get, but remains easy
|
|||
|
to follow.</p>
|
|||
|
<p>So, in all, in most lines binding a name, I wouldn’t use assignment
|
|||
|
expressions, but because that construct is so very frequent, that
|
|||
|
leaves many places I would. In most of the latter, I found a small
|
|||
|
win that adds up due to how often it occurs, and in the rest I found a
|
|||
|
moderate to major win. I’d certainly use it more often than ternary
|
|||
|
<code class="docutils literal notranslate"><span class="pre">if</span></code>, but significantly less often than augmented assignment.</p>
|
|||
|
<section id="a-numeric-example">
|
|||
|
<h3><a class="toc-backref" href="#a-numeric-example" role="doc-backlink">A numeric example</a></h3>
|
|||
|
<p>I have another example that quite impressed me at the time.</p>
|
|||
|
<p>Where all variables are positive integers, and a is at least as large
|
|||
|
as the n’th root of x, this algorithm returns the floor of the n’th
|
|||
|
root of x (and roughly doubling the number of accurate bits per
|
|||
|
iteration):</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">while</span> <span class="n">a</span> <span class="o">></span> <span class="p">(</span><span class="n">d</span> <span class="o">:=</span> <span class="n">x</span> <span class="o">//</span> <span class="n">a</span><span class="o">**</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)):</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="p">((</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">a</span> <span class="o">+</span> <span class="n">d</span><span class="p">)</span> <span class="o">//</span> <span class="n">n</span>
|
|||
|
<span class="k">return</span> <span class="n">a</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>It’s not obvious why that works, but is no more obvious in the “loop
|
|||
|
and a half” form. It’s hard to prove correctness without building on
|
|||
|
the right insight (the “arithmetic mean - geometric mean inequality”),
|
|||
|
and knowing some non-trivial things about how nested floor functions
|
|||
|
behave. That is, the challenges are in the math, not really in the
|
|||
|
coding.</p>
|
|||
|
<p>If you do know all that, then the assignment-expression form is easily
|
|||
|
read as “while the current guess is too large, get a smaller guess”,
|
|||
|
where the “too large?” test and the new guess share an expensive
|
|||
|
sub-expression.</p>
|
|||
|
<p>To my eyes, the original form is harder to understand:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
|
|||
|
<span class="n">d</span> <span class="o">=</span> <span class="n">x</span> <span class="o">//</span> <span class="n">a</span><span class="o">**</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
|
|||
|
<span class="k">if</span> <span class="n">a</span> <span class="o"><=</span> <span class="n">d</span><span class="p">:</span>
|
|||
|
<span class="k">break</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="p">((</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">a</span> <span class="o">+</span> <span class="n">d</span><span class="p">)</span> <span class="o">//</span> <span class="n">n</span>
|
|||
|
<span class="k">return</span> <span class="n">a</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="appendix-b-rough-code-translations-for-comprehensions">
|
|||
|
<h2><a class="toc-backref" href="#appendix-b-rough-code-translations-for-comprehensions" role="doc-backlink">Appendix B: Rough code translations for comprehensions</a></h2>
|
|||
|
<p>This appendix attempts to clarify (though not specify) the rules when
|
|||
|
a target occurs in a comprehension or in a generator expression.
|
|||
|
For a number of illustrative examples we show the original code,
|
|||
|
containing a comprehension, and the translation, where the
|
|||
|
comprehension has been replaced by an equivalent generator function
|
|||
|
plus some scaffolding.</p>
|
|||
|
<p>Since <code class="docutils literal notranslate"><span class="pre">[x</span> <span class="pre">for</span> <span class="pre">...]</span></code> is equivalent to <code class="docutils literal notranslate"><span class="pre">list(x</span> <span class="pre">for</span> <span class="pre">...)</span></code> these
|
|||
|
examples all use list comprehensions without loss of generality.
|
|||
|
And since these examples are meant to clarify edge cases of the rules,
|
|||
|
they aren’t trying to look like real code.</p>
|
|||
|
<p>Note: comprehensions are already implemented via synthesizing nested
|
|||
|
generator functions like those in this appendix. The new part is
|
|||
|
adding appropriate declarations to establish the intended scope of
|
|||
|
assignment expression targets (the same scope they resolve to as if
|
|||
|
the assignment were performed in the block containing the outermost
|
|||
|
comprehension). For type inference purposes, these illustrative
|
|||
|
expansions do not imply that assignment expression targets are always
|
|||
|
Optional (but they do indicate the target binding scope).</p>
|
|||
|
<p>Let’s start with a reminder of what code is generated for a generator
|
|||
|
expression without assignment expression.</p>
|
|||
|
<ul>
|
|||
|
<li>Original code (EXPR usually references VAR):<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="n">EXPR</span> <span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">ITERABLE</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Translation (let’s not worry about name conflicts):<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">genexpr</span><span class="p">(</span><span class="n">iterator</span><span class="p">):</span>
|
|||
|
<span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">iterator</span><span class="p">:</span>
|
|||
|
<span class="k">yield</span> <span class="n">EXPR</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">genexpr</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">ITERABLE</span><span class="p">)))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
<p>Let’s add a simple assignment expression.</p>
|
|||
|
<ul>
|
|||
|
<li>Original code:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="n">TARGET</span> <span class="o">:=</span> <span class="n">EXPR</span> <span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">ITERABLE</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Translation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="k">if</span> <span class="kc">False</span><span class="p">:</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># Dead code to ensure TARGET is a local variable</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">genexpr</span><span class="p">(</span><span class="n">iterator</span><span class="p">):</span>
|
|||
|
<span class="k">nonlocal</span> <span class="n">TARGET</span>
|
|||
|
<span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">iterator</span><span class="p">:</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="n">EXPR</span>
|
|||
|
<span class="k">yield</span> <span class="n">TARGET</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">genexpr</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">ITERABLE</span><span class="p">)))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
<p>Let’s add a <code class="docutils literal notranslate"><span class="pre">global</span> <span class="pre">TARGET</span></code> declaration in <code class="docutils literal notranslate"><span class="pre">f()</span></code>.</p>
|
|||
|
<ul>
|
|||
|
<li>Original code:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="k">global</span> <span class="n">TARGET</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="n">TARGET</span> <span class="o">:=</span> <span class="n">EXPR</span> <span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">ITERABLE</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Translation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="k">global</span> <span class="n">TARGET</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">genexpr</span><span class="p">(</span><span class="n">iterator</span><span class="p">):</span>
|
|||
|
<span class="k">global</span> <span class="n">TARGET</span>
|
|||
|
<span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">iterator</span><span class="p">:</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="n">EXPR</span>
|
|||
|
<span class="k">yield</span> <span class="n">TARGET</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">genexpr</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">ITERABLE</span><span class="p">)))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
<p>Or instead let’s add a <code class="docutils literal notranslate"><span class="pre">nonlocal</span> <span class="pre">TARGET</span></code> declaration in <code class="docutils literal notranslate"><span class="pre">f()</span></code>.</p>
|
|||
|
<ul>
|
|||
|
<li>Original code:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">g</span><span class="p">():</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="o">...</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="k">nonlocal</span> <span class="n">TARGET</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="n">TARGET</span> <span class="o">:=</span> <span class="n">EXPR</span> <span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">ITERABLE</span><span class="p">]</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Translation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">g</span><span class="p">():</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="o">...</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="k">nonlocal</span> <span class="n">TARGET</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">genexpr</span><span class="p">(</span><span class="n">iterator</span><span class="p">):</span>
|
|||
|
<span class="k">nonlocal</span> <span class="n">TARGET</span>
|
|||
|
<span class="k">for</span> <span class="n">VAR</span> <span class="ow">in</span> <span class="n">iterator</span><span class="p">:</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="n">EXPR</span>
|
|||
|
<span class="k">yield</span> <span class="n">TARGET</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">genexpr</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">ITERABLE</span><span class="p">)))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
<p>Finally, let’s nest two comprehensions.</p>
|
|||
|
<ul>
|
|||
|
<li>Original code:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="p">[[</span><span class="n">TARGET</span> <span class="o">:=</span> <span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">)]</span>
|
|||
|
<span class="c1"># I.e., a = [[0, 1, 2], [0, 1, 2]]</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="n">TARGET</span><span class="p">)</span> <span class="c1"># prints 2</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li>Translation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="k">if</span> <span class="kc">False</span><span class="p">:</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="kc">None</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">outer_genexpr</span><span class="p">(</span><span class="n">outer_iterator</span><span class="p">):</span>
|
|||
|
<span class="k">nonlocal</span> <span class="n">TARGET</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">inner_generator</span><span class="p">(</span><span class="n">inner_iterator</span><span class="p">):</span>
|
|||
|
<span class="k">nonlocal</span> <span class="n">TARGET</span>
|
|||
|
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">inner_iterator</span><span class="p">:</span>
|
|||
|
<span class="n">TARGET</span> <span class="o">=</span> <span class="n">i</span>
|
|||
|
<span class="k">yield</span> <span class="n">i</span>
|
|||
|
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">outer_iterator</span><span class="p">:</span>
|
|||
|
<span class="k">yield</span> <span class="nb">list</span><span class="p">(</span><span class="n">inner_generator</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)))</span>
|
|||
|
<span class="n">a</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">outer_genexpr</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">)))</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="n">TARGET</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</section>
|
|||
|
<section id="appendix-c-no-changes-to-scope-semantics">
|
|||
|
<h2><a class="toc-backref" href="#appendix-c-no-changes-to-scope-semantics" role="doc-backlink">Appendix C: No Changes to Scope Semantics</a></h2>
|
|||
|
<p>Because it has been a point of confusion, note that nothing about Python’s
|
|||
|
scoping semantics is changed. Function-local scopes continue to be resolved
|
|||
|
at compile time, and to have indefinite temporal extent at run time (“full
|
|||
|
closures”). Example:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">=</span> <span class="mi">42</span>
|
|||
|
<span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">():</span>
|
|||
|
<span class="c1"># `a` is local to `f`, but remains unbound</span>
|
|||
|
<span class="c1"># until the caller executes this genexp:</span>
|
|||
|
<span class="k">yield</span> <span class="p">((</span><span class="n">a</span> <span class="o">:=</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span>
|
|||
|
<span class="k">yield</span> <span class="k">lambda</span><span class="p">:</span> <span class="n">a</span> <span class="o">+</span> <span class="mi">100</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"done"</span><span class="p">)</span>
|
|||
|
<span class="k">try</span><span class="p">:</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"`a` is bound to </span><span class="si">{</span><span class="n">a</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
|||
|
<span class="k">assert</span> <span class="kc">False</span>
|
|||
|
<span class="k">except</span> <span class="ne">UnboundLocalError</span><span class="p">:</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s2">"`a` is not yet bound"</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Then:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">results</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">f</span><span class="p">())</span> <span class="c1"># [genexp, lambda]</span>
|
|||
|
<span class="go">done</span>
|
|||
|
<span class="go">`a` is not yet bound</span>
|
|||
|
<span class="go"># The execution frame for f no longer exists in CPython,</span>
|
|||
|
<span class="go"># but f's locals live so long as they can still be referenced.</span>
|
|||
|
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">type</span><span class="p">,</span> <span class="n">results</span><span class="p">))</span>
|
|||
|
<span class="go">[<class 'generator'>, <class 'function'>]</span>
|
|||
|
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
|||
|
<span class="go">[0, 1, 2]</span>
|
|||
|
<span class="gp">>>> </span><span class="n">results</span><span class="p">[</span><span class="mi">1</span><span class="p">]()</span>
|
|||
|
<span class="go">102</span>
|
|||
|
<span class="gp">>>> </span><span class="n">a</span>
|
|||
|
<span class="go">42</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</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="id4" role="doc-footnote">
|
|||
|
<dt class="label" id="id4">[<a href="#id1">1</a>]</dt>
|
|||
|
<dd>Proof of concept implementation
|
|||
|
(<a class="reference external" href="https://github.com/Rosuav/cpython/tree/assignment-expressions">https://github.com/Rosuav/cpython/tree/assignment-expressions</a>)</aside>
|
|||
|
<aside class="footnote brackets" id="id5" role="doc-footnote">
|
|||
|
<dt class="label" id="id5">[<a href="#id3">2</a>]</dt>
|
|||
|
<dd>Pivotal post regarding inline assignment semantics
|
|||
|
(<a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2018-March/049409.html">https://mail.python.org/pipermail/python-ideas/2018-March/049409.html</a>)</aside>
|
|||
|
<aside class="footnote brackets" id="id6" role="doc-footnote">
|
|||
|
<dt class="label" id="id6">[<a href="#id2">3</a>]</dt>
|
|||
|
<dd>Discussion of PEP 572 TargetScopeError
|
|||
|
(<a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/thread/FXVSYCTQOTT7JCFACKPGPXKULBCGEPQY/">https://mail.python.org/archives/list/python-dev@python.org/thread/FXVSYCTQOTT7JCFACKPGPXKULBCGEPQY/</a>)</aside>
|
|||
|
</aside>
|
|||
|
</section>
|
|||
|
<section id="copyright">
|
|||
|
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
|||
|
<p>This document has been placed in the public domain.</p>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<hr class="docutils" />
|
|||
|
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0572.rst">https://github.com/python/peps/blob/main/peps/pep-0572.rst</a></p>
|
|||
|
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0572.rst">2023-10-11 12:05:51 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="#rationale">Rationale</a><ul>
|
|||
|
<li><a class="reference internal" href="#the-importance-of-real-code">The importance of real code</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#syntax-and-semantics">Syntax and semantics</a><ul>
|
|||
|
<li><a class="reference internal" href="#exceptional-cases">Exceptional cases</a></li>
|
|||
|
<li><a class="reference internal" href="#scope-of-the-target">Scope of the target</a></li>
|
|||
|
<li><a class="reference internal" href="#relative-precedence-of">Relative precedence of <code class="docutils literal notranslate"><span class="pre">:=</span></code></a></li>
|
|||
|
<li><a class="reference internal" href="#change-to-evaluation-order">Change to evaluation order</a></li>
|
|||
|
<li><a class="reference internal" href="#differences-between-assignment-expressions-and-assignment-statements">Differences between assignment expressions and assignment statements</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#specification-changes-during-implementation">Specification changes during implementation</a></li>
|
|||
|
<li><a class="reference internal" href="#examples">Examples</a><ul>
|
|||
|
<li><a class="reference internal" href="#examples-from-the-python-standard-library">Examples from the Python standard library</a><ul>
|
|||
|
<li><a class="reference internal" href="#site-py">site.py</a></li>
|
|||
|
<li><a class="reference internal" href="#pydecimal-py">_pydecimal.py</a></li>
|
|||
|
<li><a class="reference internal" href="#copy-py">copy.py</a></li>
|
|||
|
<li><a class="reference internal" href="#datetime-py">datetime.py</a></li>
|
|||
|
<li><a class="reference internal" href="#sysconfig-py">sysconfig.py</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#simplifying-list-comprehensions">Simplifying list comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#capturing-condition-values">Capturing condition values</a></li>
|
|||
|
<li><a class="reference internal" href="#fork">Fork</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#rejected-alternative-proposals">Rejected alternative proposals</a><ul>
|
|||
|
<li><a class="reference internal" href="#changing-the-scope-rules-for-comprehensions">Changing the scope rules for comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#alternative-spellings">Alternative spellings</a></li>
|
|||
|
<li><a class="reference internal" href="#special-casing-conditional-statements">Special-casing conditional statements</a></li>
|
|||
|
<li><a class="reference internal" href="#special-casing-comprehensions">Special-casing comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#lowering-operator-precedence">Lowering operator precedence</a></li>
|
|||
|
<li><a class="reference internal" href="#allowing-commas-to-the-right">Allowing commas to the right</a></li>
|
|||
|
<li><a class="reference internal" href="#always-requiring-parentheses">Always requiring parentheses</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#frequently-raised-objections">Frequently Raised Objections</a><ul>
|
|||
|
<li><a class="reference internal" href="#why-not-just-turn-existing-assignment-into-an-expression">Why not just turn existing assignment into an expression?</a></li>
|
|||
|
<li><a class="reference internal" href="#with-assignment-expressions-why-bother-with-assignment-statements">With assignment expressions, why bother with assignment statements?</a></li>
|
|||
|
<li><a class="reference internal" href="#why-not-use-a-sublocal-scope-and-prevent-namespace-pollution">Why not use a sublocal scope and prevent namespace pollution?</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#style-guide-recommendations">Style guide recommendations</a></li>
|
|||
|
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
|||
|
<li><a class="reference internal" href="#appendix-a-tim-peters-s-findings">Appendix A: Tim Peters’s findings</a><ul>
|
|||
|
<li><a class="reference internal" href="#a-numeric-example">A numeric example</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#appendix-b-rough-code-translations-for-comprehensions">Appendix B: Rough code translations for comprehensions</a></li>
|
|||
|
<li><a class="reference internal" href="#appendix-c-no-changes-to-scope-semantics">Appendix C: No Changes to Scope Semantics</a></li>
|
|||
|
<li><a class="reference internal" href="#references">References</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-0572.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>
|