421 lines
36 KiB
HTML
421 lines
36 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 203 – Augmented Assignments | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0203/">
|
||
<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 203 – Augmented Assignments | peps.python.org'>
|
||
<meta property="og:description" content="This PEP describes the augmented assignment proposal for Python 2.0. This PEP tracks the status and ownership of this feature, slated for introduction in Python 2.0. It contains a description of the feature and outlines changes necessary to support th...">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0203/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="This PEP describes the augmented assignment proposal for Python 2.0. This PEP tracks the status and ownership of this feature, slated for introduction in Python 2.0. It contains a description of the feature and outlines changes necessary to support th...">
|
||
<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 203</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 203 – Augmented Assignments</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Thomas Wouters <thomas 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">13-Jul-2000</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">2.0</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">14-Aug-2000</dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#introduction">Introduction</a></li>
|
||
<li><a class="reference internal" href="#proposed-semantics">Proposed Semantics</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#new-methods">New methods</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#open-issues">Open Issues</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="introduction">
|
||
<h2><a class="toc-backref" href="#introduction" role="doc-backlink">Introduction</a></h2>
|
||
<p>This PEP describes the <em>augmented assignment</em> proposal for Python 2.0. This
|
||
PEP tracks the status and ownership of this feature, slated for introduction
|
||
in Python 2.0. It contains a description of the feature and outlines changes
|
||
necessary to support the feature. This PEP summarizes discussions held in
|
||
mailing list forums <a class="footnote-reference brackets" href="#id3" id="id1">[1]</a>, and provides URLs for further information where
|
||
appropriate. The CVS revision history of this file contains the definitive
|
||
historical record.</p>
|
||
</section>
|
||
<section id="proposed-semantics">
|
||
<h2><a class="toc-backref" href="#proposed-semantics" role="doc-backlink">Proposed Semantics</a></h2>
|
||
<p>The proposed patch that adds augmented assignment to Python introduces the
|
||
following new operators:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">+=</span> <span class="o">-=</span> <span class="o">*=</span> <span class="o">/=</span> <span class="o">%=</span> <span class="o">**=</span> <span class="o"><<=</span> <span class="o">>>=</span> <span class="o">&=</span> <span class="o">^=</span> <span class="o">|=</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>They implement the same operator as their normal binary form, except that the
|
||
operation is done <em>in-place</em> when the left-hand side object supports it, and
|
||
that the left-hand side is only evaluated once.</p>
|
||
<p>They truly behave as augmented assignment, in that they perform all of the
|
||
normal load and store operations, in addition to the binary operation they are
|
||
intended to do. So, given the expression:</p>
|
||
<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>
|
||
</pre></div>
|
||
</div>
|
||
<p>The object <code class="docutils literal notranslate"><span class="pre">x</span></code> is loaded, then <code class="docutils literal notranslate"><span class="pre">y</span></code> is added to it, and the resulting
|
||
object is stored back in the original place. The precise action performed on
|
||
the two arguments depends on the type of <code class="docutils literal notranslate"><span class="pre">x</span></code>, and possibly of <code class="docutils literal notranslate"><span class="pre">y</span></code>.</p>
|
||
<p>The idea behind augmented assignment in Python is that it isn’t just an easier
|
||
way to write the common practice of storing the result of a binary operation
|
||
in its left-hand operand, but also a way for the left-hand operand in question
|
||
to know that it should operate <em>on itself</em>, rather than creating a modified
|
||
copy of itself.</p>
|
||
<p>To make this possible, a number of new <em>hooks</em> are added to Python classes and
|
||
C extension types, which are called when the object in question is used as the
|
||
left hand side of an augmented assignment operation. If the class or type
|
||
does not implement the <em>in-place</em> hooks, the normal hooks for the particular
|
||
binary operation are used.</p>
|
||
<p>So, given an instance object <code class="docutils literal notranslate"><span class="pre">x</span></code>, the expression:</p>
|
||
<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>
|
||
</pre></div>
|
||
</div>
|
||
<p>tries to call <code class="docutils literal notranslate"><span class="pre">x.__iadd__(y)</span></code>, which is the <em>in-place</em> variant of
|
||
<code class="docutils literal notranslate"><span class="pre">__add__</span></code> . If <code class="docutils literal notranslate"><span class="pre">__iadd__</span></code> is not present, <code class="docutils literal notranslate"><span class="pre">x.__add__(y)</span></code> is attempted,
|
||
and finally <code class="docutils literal notranslate"><span class="pre">y.__radd__(x)</span></code> if <code class="docutils literal notranslate"><span class="pre">__add__</span></code> is missing too. There is no
|
||
<em>right-hand-side</em> variant of <code class="docutils literal notranslate"><span class="pre">__iadd__</span></code>, because that would require for
|
||
<code class="docutils literal notranslate"><span class="pre">y</span></code> to know how to in-place modify <code class="docutils literal notranslate"><span class="pre">x</span></code>, which is unsafe to say the least.
|
||
The <code class="docutils literal notranslate"><span class="pre">__iadd__</span></code> hook should behave similar to <code class="docutils literal notranslate"><span class="pre">__add__</span></code>, returning the
|
||
result of the operation (which could be <code class="docutils literal notranslate"><span class="pre">self</span></code>) which is to be assigned to
|
||
the variable <code class="docutils literal notranslate"><span class="pre">x</span></code>.</p>
|
||
<p>For C extension types, the <em>hooks</em> are members of the <code class="docutils literal notranslate"><span class="pre">PyNumberMethods</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">PySequenceMethods</span></code> structures. Some special semantics apply to make the
|
||
use of these methods, and the mixing of Python instance objects and C types,
|
||
as unsurprising as possible.</p>
|
||
<p>In the generic case of <code class="docutils literal notranslate"><span class="pre">x</span> <span class="pre"><augop></span> <span class="pre">y</span></code> (or a similar case using the
|
||
<code class="docutils literal notranslate"><span class="pre">PyNumber_InPlace</span></code> API functions) the principal object being operated on is
|
||
<code class="docutils literal notranslate"><span class="pre">x</span></code>. This differs from normal binary operations, where <code class="docutils literal notranslate"><span class="pre">x</span></code> and <code class="docutils literal notranslate"><span class="pre">y</span></code>
|
||
could be considered <em>co-operating</em>, because unlike in binary operations, the
|
||
operands in an in-place operation cannot be swapped. However, in-place
|
||
operations do fall back to normal binary operations when in-place modification
|
||
is not supported, resulting in the following rules:</p>
|
||
<ul>
|
||
<li>If the left-hand object (<code class="docutils literal notranslate"><span class="pre">x</span></code>) is an instance object, and it has a
|
||
<code class="docutils literal notranslate"><span class="pre">__coerce__</span></code> method, call that function with <code class="docutils literal notranslate"><span class="pre">y</span></code> as the argument. If
|
||
coercion succeeds, and the resulting left-hand object is a different object
|
||
than <code class="docutils literal notranslate"><span class="pre">x</span></code>, stop processing it as in-place and call the appropriate function
|
||
for the normal binary operation, with the coerced <code class="docutils literal notranslate"><span class="pre">x</span></code> and <code class="docutils literal notranslate"><span class="pre">y</span></code> as
|
||
arguments. The result of the operation is whatever that function returns.<p>If coercion does not yield a different object for <code class="docutils literal notranslate"><span class="pre">x</span></code>, or <code class="docutils literal notranslate"><span class="pre">x</span></code> does not
|
||
define a <code class="docutils literal notranslate"><span class="pre">__coerce__</span></code> method, and <code class="docutils literal notranslate"><span class="pre">x</span></code> has the appropriate <code class="docutils literal notranslate"><span class="pre">__ihook__</span></code>
|
||
for this operation, call that method with <code class="docutils literal notranslate"><span class="pre">y</span></code> as the argument, and the
|
||
result of the operation is whatever that method returns.</p>
|
||
</li>
|
||
<li>Otherwise, if the left-hand object is not an instance object, but its type
|
||
does define the in-place function for this operation, call that function
|
||
with <code class="docutils literal notranslate"><span class="pre">x</span></code> and <code class="docutils literal notranslate"><span class="pre">y</span></code> as the arguments, and the result of the operation is
|
||
whatever that function returns.<p>Note that no coercion on either <code class="docutils literal notranslate"><span class="pre">x</span></code> or <code class="docutils literal notranslate"><span class="pre">y</span></code> is done in this case, and
|
||
it’s perfectly valid for a C type to receive an instance object as the
|
||
second argument; that is something that cannot happen with normal binary
|
||
operations.</p>
|
||
</li>
|
||
<li>Otherwise, process it exactly as a normal binary operation (not in-place),
|
||
including argument coercion. In short, if either argument is an instance
|
||
object, resolve the operation through <code class="docutils literal notranslate"><span class="pre">__coerce__</span></code>, <code class="docutils literal notranslate"><span class="pre">__hook__</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">__rhook__</span></code>. Otherwise, both objects are C types, and they are coerced
|
||
and passed to the appropriate function.</li>
|
||
<li>If no way to process the operation can be found, raise a <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> with
|
||
an error message specific to the operation.</li>
|
||
<li>Some special casing exists to account for the case of <code class="docutils literal notranslate"><span class="pre">+</span></code> and <code class="docutils literal notranslate"><span class="pre">*</span></code>,
|
||
which have a special meaning for sequences: for <code class="docutils literal notranslate"><span class="pre">+</span></code>, sequence
|
||
concatenation, no coercion what so ever is done if a C type defines
|
||
<code class="docutils literal notranslate"><span class="pre">sq_concat</span></code> or <code class="docutils literal notranslate"><span class="pre">sq_inplace_concat</span></code>. For <code class="docutils literal notranslate"><span class="pre">*</span></code>, sequence repeating,
|
||
<code class="docutils literal notranslate"><span class="pre">y</span></code> is converted to a C integer before calling either
|
||
<code class="docutils literal notranslate"><span class="pre">sq_inplace_repeat</span></code> and <code class="docutils literal notranslate"><span class="pre">sq_repeat</span></code>. This is done even if <code class="docutils literal notranslate"><span class="pre">y</span></code> is an
|
||
instance, though not if <code class="docutils literal notranslate"><span class="pre">x</span></code> is an instance.</li>
|
||
</ul>
|
||
<p>The in-place function should always return a new reference, either to the
|
||
old <code class="docutils literal notranslate"><span class="pre">x</span></code> object if the operation was indeed performed in-place, or to a new
|
||
object.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>There are two main reasons for adding this feature to Python: simplicity of
|
||
expression, and support for in-place operations. The end result is a tradeoff
|
||
between simplicity of syntax and simplicity of expression; like most new
|
||
features, augmented assignment doesn’t add anything that was previously
|
||
impossible. It merely makes these things easier to do.</p>
|
||
<p>Adding augmented assignment will make Python’s syntax more complex. Instead
|
||
of a single assignment operation, there are now twelve assignment operations,
|
||
eleven of which also perform a binary operation. However, these eleven new
|
||
forms of assignment are easy to understand as the coupling between assignment
|
||
and the binary operation, and they require no large conceptual leap to
|
||
understand. Furthermore, languages that do have augmented assignment have
|
||
shown that they are a popular, much used feature. Expressions of the form:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o"><</span><span class="n">x</span><span class="o">></span> <span class="o">=</span> <span class="o"><</span><span class="n">x</span><span class="o">></span> <span class="o"><</span><span class="n">operator</span><span class="o">></span> <span class="o"><</span><span class="n">y</span><span class="o">></span>
|
||
</pre></div>
|
||
</div>
|
||
<p>are common enough in those languages to make the extra syntax worthwhile, and
|
||
Python does not have significantly fewer of those expressions. Quite the
|
||
opposite, in fact, since in Python you can also concatenate lists with a
|
||
binary operator, something that is done quite frequently. Writing the above
|
||
expression as:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o"><</span><span class="n">x</span><span class="o">></span> <span class="o"><</span><span class="n">operator</span><span class="o">>=</span> <span class="o"><</span><span class="n">y</span><span class="o">></span>
|
||
</pre></div>
|
||
</div>
|
||
<p>is both more readable and less error prone, because it is instantly obvious to
|
||
the reader that it is <code class="docutils literal notranslate"><span class="pre"><x></span></code> that is being changed, and not <code class="docutils literal notranslate"><span class="pre"><x></span></code> that is
|
||
being replaced by something almost, but not quite, entirely unlike <code class="docutils literal notranslate"><span class="pre"><x></span></code>.</p>
|
||
<p>The new in-place operations are especially useful to matrix calculation and
|
||
other applications that require large objects. In order to efficiently deal
|
||
with the available program memory, such packages cannot blindly use the
|
||
current binary operations. Because these operations always create a new
|
||
object, adding a single item to an existing (large) object would result in
|
||
copying the entire object (which may cause the application to run out of
|
||
memory), add the single item, and then possibly delete the original object,
|
||
depending on reference count.</p>
|
||
<p>To work around this problem, the packages currently have to use methods or
|
||
functions to modify an object in-place, which is definitely less readable than
|
||
an augmented assignment expression. Augmented assignment won’t solve all the
|
||
problems for these packages, since some operations cannot be expressed in the
|
||
limited set of binary operators to start with, but it is a start. <a class="pep reference internal" href="../pep-0211/" title="PEP 211 – Adding A New Outer Product Operator">PEP 211</a>
|
||
is looking at adding new operators.</p>
|
||
</section>
|
||
<section id="new-methods">
|
||
<h2><a class="toc-backref" href="#new-methods" role="doc-backlink">New methods</a></h2>
|
||
<p>The proposed implementation adds the following 11 possible <em>hooks</em> which
|
||
Python classes can implement to overload the augmented assignment operations:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="fm">__iadd__</span>
|
||
<span class="fm">__isub__</span>
|
||
<span class="fm">__imul__</span>
|
||
<span class="n">__idiv__</span>
|
||
<span class="fm">__imod__</span>
|
||
<span class="fm">__ipow__</span>
|
||
<span class="fm">__ilshift__</span>
|
||
<span class="fm">__irshift__</span>
|
||
<span class="fm">__iand__</span>
|
||
<span class="fm">__ixor__</span>
|
||
<span class="fm">__ior__</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <em>i</em> in <code class="docutils literal notranslate"><span class="pre">__iadd__</span></code> stands for <em>in-place</em>.</p>
|
||
<p>For C extension types, the following struct members are added.</p>
|
||
<p>To <code class="docutils literal notranslate"><span class="pre">PyNumberMethods</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">binaryfunc</span> <span class="n">nb_inplace_add</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_subtract</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_multiply</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_divide</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_remainder</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_power</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_lshift</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_rshift</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_and</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_xor</span><span class="p">;</span>
|
||
<span class="n">binaryfunc</span> <span class="n">nb_inplace_or</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To <code class="docutils literal notranslate"><span class="pre">PySequenceMethods</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">binaryfunc</span> <span class="n">sq_inplace_concat</span><span class="p">;</span>
|
||
<span class="n">intargfunc</span> <span class="n">sq_inplace_repeat</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In order to keep binary compatibility, the <code class="docutils literal notranslate"><span class="pre">tp_flags</span></code> TypeObject member is
|
||
used to determine whether the TypeObject in question has allocated room for
|
||
these slots. Until a clean break in binary compatibility is made (which may
|
||
or may not happen before 2.0) code that wants to use one of the new struct
|
||
members must first check that they are available with the
|
||
<code class="docutils literal notranslate"><span class="pre">PyType_HasFeature()</span></code> macro:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="p">(</span><span class="n">PyType_HasFeature</span><span class="p">(</span><span class="n">x</span><span class="o">-></span><span class="n">ob_type</span><span class="p">,</span> <span class="n">Py_TPFLAGS_HAVE_INPLACE_OPS</span><span class="p">)</span> <span class="o">&&</span>
|
||
<span class="n">x</span><span class="o">-></span><span class="n">ob_type</span><span class="o">-></span><span class="n">tp_as_number</span> <span class="o">&&</span> <span class="n">x</span><span class="o">-></span><span class="n">ob_type</span><span class="o">-></span><span class="n">tp_as_number</span><span class="o">-></span><span class="n">nb_inplace_add</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="o">/*</span> <span class="o">...</span> <span class="o">*/</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This check must be made even before testing the method slots for <code class="docutils literal notranslate"><span class="pre">NULL</span></code>
|
||
values! The macro only tests whether the slots are available, not whether
|
||
they are filled with methods or not.</p>
|
||
</section>
|
||
<section id="implementation">
|
||
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
|
||
<p>The current implementation of augmented assignment <a class="footnote-reference brackets" href="#id4" id="id2">[2]</a> adds, in addition to
|
||
the methods and slots already covered, 13 new bytecodes and 13 new API
|
||
functions.</p>
|
||
<p>The API functions are simply in-place versions of the current binary-operation
|
||
API functions:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyNumber_InPlaceAdd</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceSubtract</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceMultiply</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceDivide</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceRemainder</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlacePower</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceLshift</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceRshift</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceAnd</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceXor</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PyNumber_InPlaceOr</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PySequence_InPlaceConcat</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o1</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o2</span><span class="p">);</span>
|
||
<span class="n">PySequence_InPlaceRepeat</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">o</span><span class="p">,</span> <span class="nb">int</span> <span class="n">count</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>They call either the Python class hooks (if either of the objects is a Python
|
||
class instance) or the C type’s number or sequence methods.</p>
|
||
<p>The new bytecodes are:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">INPLACE_ADD</span>
|
||
<span class="n">INPLACE_SUBTRACT</span>
|
||
<span class="n">INPLACE_MULTIPLY</span>
|
||
<span class="n">INPLACE_DIVIDE</span>
|
||
<span class="n">INPLACE_REMAINDER</span>
|
||
<span class="n">INPLACE_POWER</span>
|
||
<span class="n">INPLACE_LEFTSHIFT</span>
|
||
<span class="n">INPLACE_RIGHTSHIFT</span>
|
||
<span class="n">INPLACE_AND</span>
|
||
<span class="n">INPLACE_XOR</span>
|
||
<span class="n">INPLACE_OR</span>
|
||
<span class="n">ROT_FOUR</span>
|
||
<span class="n">DUP_TOPX</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">INPLACE_*</span></code> bytecodes mirror the <code class="docutils literal notranslate"><span class="pre">BINARY_*</span></code> bytecodes, except that
|
||
they are implemented as calls to the <code class="docutils literal notranslate"><span class="pre">InPlace</span></code> API functions. The other two
|
||
bytecodes are <em>utility</em> bytecodes: <code class="docutils literal notranslate"><span class="pre">ROT_FOUR</span></code> behaves like <code class="docutils literal notranslate"><span class="pre">ROT_THREE</span></code>
|
||
except that the four topmost stack items are rotated.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">DUP_TOPX</span></code> is a bytecode that takes a single argument, which should be an
|
||
integer between 1 and 5 (inclusive) which is the number of items to duplicate
|
||
in one block. Given a stack like this (where the right side of the list is
|
||
the <em>top</em> of the stack):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">DUP_TOPX</span> <span class="pre">3</span></code> would duplicate the top 3 items, resulting in this stack:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">DUP_TOPX</span></code> with an argument of 1 is the same as <code class="docutils literal notranslate"><span class="pre">DUP_TOP</span></code>. The limit of 5
|
||
is purely an implementation limit . The implementation of augmented
|
||
assignment requires only <code class="docutils literal notranslate"><span class="pre">DUP_TOPX</span></code> with an argument of 2 and 3, and could
|
||
do without this new opcode at the cost of a fair number of <code class="docutils literal notranslate"><span class="pre">DUP_TOP</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">ROT_*</span></code>.</p>
|
||
</section>
|
||
<section id="open-issues">
|
||
<h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">PyNumber_InPlace</span></code> API is only a subset of the normal <code class="docutils literal notranslate"><span class="pre">PyNumber</span></code> API:
|
||
only those functions that are required to support the augmented assignment
|
||
syntax are included. If other in-place API functions are needed, they can be
|
||
added later.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">DUP_TOPX</span></code> bytecode is a conveniency bytecode, and is not actually
|
||
necessary. It should be considered whether this bytecode is worth having.
|
||
There seems to be no other possible use for this bytecode at this time.</p>
|
||
</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 id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id3" role="doc-footnote">
|
||
<dt class="label" id="id3">[<a href="#id1">1</a>]</dt>
|
||
<dd><a class="reference external" href="http://www.python.org/pipermail/python-list/2000-June/059556.html">http://www.python.org/pipermail/python-list/2000-June/059556.html</a></aside>
|
||
<aside class="footnote brackets" id="id4" role="doc-footnote">
|
||
<dt class="label" id="id4">[<a href="#id2">2</a>]</dt>
|
||
<dd><a class="reference external" href="http://sourceforge.net/patch?func=detailpatch&patch_id=100699&group_id=5470">http://sourceforge.net/patch?func=detailpatch&patch_id=100699&group_id=5470</a></aside>
|
||
</aside>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0203.rst">https://github.com/python/peps/blob/main/peps/pep-0203.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0203.rst">2023-09-09 17:39:29 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#introduction">Introduction</a></li>
|
||
<li><a class="reference internal" href="#proposed-semantics">Proposed Semantics</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#new-methods">New methods</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#open-issues">Open Issues</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0203.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> |