peps/pep-0225/index.html

843 lines
79 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 225 Elementwise/Objectwise Operators | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0225/">
<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 225 Elementwise/Objectwise Operators | peps.python.org'>
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0225/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="Python Enhancement Proposals (PEPs)">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 225</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 225 Elementwise/Objectwise Operators</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Huaiyu Zhu &lt;hzhu&#32;&#97;t&#32;users.sourceforge.net&gt;,
Gregory Lielens &lt;gregory.lielens&#32;&#97;t&#32;fft.be&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</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">19-Sep-2000</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">2.1</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even"><p></p></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="#background">Background</a></li>
<li><a class="reference internal" href="#proposed-extension">Proposed extension</a></li>
<li><a class="reference internal" href="#prototype-implementation">Prototype Implementation</a></li>
<li><a class="reference internal" href="#alternatives-to-adding-new-operators">Alternatives to adding new operators</a></li>
<li><a class="reference internal" href="#alternative-forms-of-infix-operators">Alternative forms of infix operators</a></li>
<li><a class="reference internal" href="#semantics-of-new-operators">Semantics of new operators</a></li>
<li><a class="reference internal" href="#examples">Examples</a></li>
<li><a class="reference internal" href="#miscellaneous-issues">Miscellaneous issues</a></li>
<li><a class="reference internal" href="#impact-on-general-elementization">Impact on general elementization</a><ul>
<li><a class="reference internal" href="#notes">Notes</a></li>
</ul>
</li>
<li><a class="reference internal" href="#impact-on-named-operators">Impact on named operators</a></li>
<li><a class="reference internal" href="#credits-and-archives">Credits and archives</a></li>
<li><a class="reference internal" href="#additional-references">Additional References</a></li>
</ul>
</details></section>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The approach in the later <a class="pep reference internal" href="../pep-0465/" title="PEP 465 A dedicated infix operator for matrix multiplication">PEP 465</a> was eventually accepted
in lieu of this PEP. The <a class="pep reference internal" href="../pep-0465/#rejected-alternatives-to-adding-a-new-operator" title="PEP 465 A dedicated infix operator for matrix multiplication § Rejected alternatives to adding a new operator">Rejected Ideas</a>
of that PEP explains the rationale in more detail.</p>
</div>
<section id="introduction">
<h2><a class="toc-backref" href="#introduction" role="doc-backlink">Introduction</a></h2>
<p>This PEP describes a proposal to add new operators to Python which are useful
for distinguishing elementwise and objectwise operations, and summarizes
discussions in the news group comp.lang.python on this topic. See Credits and
Archives section at end. Issues discussed here include:</p>
<ul class="simple">
<li>Background.</li>
<li>Description of proposed operators and implementation issues.</li>
<li>Analysis of alternatives to new operators.</li>
<li>Analysis of alternative forms.</li>
<li>Compatibility issues</li>
<li>Description of wider extensions and other related ideas.</li>
</ul>
<p>A substantial portion of this PEP describes ideas that do not go into the
proposed extension. They are presented because the extension is essentially
syntactic sugar, so its adoption must be weighed against various possible
alternatives. While many alternatives may be better in some aspects, the
current proposal appears to be overall advantageous.</p>
<p>The issues concerning elementwise-objectwise operations extends to wider areas
than numerical computation. This document also describes how the current
proposal may be integrated with more general future extensions.</p>
</section>
<section id="background">
<h2><a class="toc-backref" href="#background" role="doc-backlink">Background</a></h2>
<p>Python provides six binary infix math operators: <code class="docutils literal notranslate"><span class="pre">+</span></code> <code class="docutils literal notranslate"><span class="pre">-</span></code> <code class="docutils literal notranslate"><span class="pre">*</span></code> <code class="docutils literal notranslate"><span class="pre">/</span></code> <code class="docutils literal notranslate"><span class="pre">%</span></code>
<code class="docutils literal notranslate"><span class="pre">**</span></code> hereafter generically represented by <code class="docutils literal notranslate"><span class="pre">op</span></code>. They can be overloaded
with new semantics for user-defined classes. However, for objects composed of
homogeneous elements, such as arrays, vectors and matrices in numerical
computation, there are two essentially distinct flavors of semantics. The
objectwise operations treat these objects as points in multidimensional spaces.
The elementwise operations treat them as collections of individual elements.
These two flavors of operations are often intermixed in the same formulas,
thereby requiring syntactical distinction.</p>
<p>Many numerical computation languages provide two sets of math operators. For
example, in MatLab, the ordinary <code class="docutils literal notranslate"><span class="pre">op</span></code> is used for objectwise operation while
<code class="docutils literal notranslate"><span class="pre">.op</span></code> is used for elementwise operation. In R, <code class="docutils literal notranslate"><span class="pre">op</span></code> stands for elementwise
operation while <code class="docutils literal notranslate"><span class="pre">%op%</span></code> stands for objectwise operation.</p>
<p>In Python, there are other methods of representation, some of which already
used by available numerical packages, such as:</p>
<ul class="simple">
<li>function: mul(a,b)</li>
<li>method: a.mul(b)</li>
<li>casting: a.E*b</li>
</ul>
<p>In several aspects these are not as adequate as infix operators. More details
will be shown later, but the key points are:</p>
<ul class="simple">
<li>Readability: Even for moderately complicated formulas, infix operators are
much cleaner than alternatives.</li>
<li>Familiarity: Users are familiar with ordinary math operators.</li>
<li>Implementation: New infix operators will not unduly clutter Python syntax.
They will greatly ease the implementation of numerical packages.</li>
</ul>
<p>While it is possible to assign current math operators to one flavor of
semantics, there is simply not enough infix operators to overload for the other
flavor. It is also impossible to maintain visual symmetry between these two
flavors if one of them does not contain symbols for ordinary math operators.</p>
</section>
<section id="proposed-extension">
<h2><a class="toc-backref" href="#proposed-extension" role="doc-backlink">Proposed extension</a></h2>
<ul class="simple">
<li>Six new binary infix operators <code class="docutils literal notranslate"><span class="pre">~+</span></code> <code class="docutils literal notranslate"><span class="pre">~-</span></code> <code class="docutils literal notranslate"><span class="pre">~*</span></code> <code class="docutils literal notranslate"><span class="pre">~/</span></code> <code class="docutils literal notranslate"><span class="pre">~%</span></code> <code class="docutils literal notranslate"><span class="pre">~**</span></code> are
added to core Python. They parallel the existing operators <code class="docutils literal notranslate"><span class="pre">+</span></code> <code class="docutils literal notranslate"><span class="pre">-</span></code> <code class="docutils literal notranslate"><span class="pre">*</span></code>
<code class="docutils literal notranslate"><span class="pre">/</span></code> <code class="docutils literal notranslate"><span class="pre">%</span></code> <code class="docutils literal notranslate"><span class="pre">**</span></code>.</li>
<li>Six augmented assignment operators <code class="docutils literal notranslate"><span class="pre">~+=</span></code> <code class="docutils literal notranslate"><span class="pre">~-=</span></code> <code class="docutils literal notranslate"><span class="pre">~*=</span></code> <code class="docutils literal notranslate"><span class="pre">~/=</span></code> <code class="docutils literal notranslate"><span class="pre">~%=</span></code>
<code class="docutils literal notranslate"><span class="pre">~**=</span></code> are added to core Python. They parallel the operators <code class="docutils literal notranslate"><span class="pre">+=</span></code> <code class="docutils literal notranslate"><span class="pre">-=</span></code>
<code class="docutils literal notranslate"><span class="pre">*=</span></code> <code class="docutils literal notranslate"><span class="pre">/=</span></code> <code class="docutils literal notranslate"><span class="pre">%=</span></code> <code class="docutils literal notranslate"><span class="pre">**=</span></code> available in Python 2.0.</li>
<li>Operator <code class="docutils literal notranslate"><span class="pre">~op</span></code> retains the syntactical properties of operator <code class="docutils literal notranslate"><span class="pre">op</span></code>,
including precedence.</li>
<li>Operator <code class="docutils literal notranslate"><span class="pre">~op</span></code> retains the semantical properties of operator <code class="docutils literal notranslate"><span class="pre">op</span></code> on
built-in number types.</li>
<li>Operator <code class="docutils literal notranslate"><span class="pre">~op</span></code> raise syntax error on non-number builtin types. This is
temporary until the proper behavior can be agreed upon.</li>
<li>These operators are overloadable in classes with names that prepend <em>t</em> (for
tilde) to names of ordinary math operators. For example, <code class="docutils literal notranslate"><span class="pre">__tadd__</span></code> and
<code class="docutils literal notranslate"><span class="pre">__rtadd__</span></code> work for <code class="docutils literal notranslate"><span class="pre">~+</span></code> just as <code class="docutils literal notranslate"><span class="pre">__add__</span></code> and <code class="docutils literal notranslate"><span class="pre">__radd__</span></code> work for
<code class="docutils literal notranslate"><span class="pre">+</span></code>.</li>
<li>As with existing operators, the <code class="docutils literal notranslate"><span class="pre">__r*__()</span></code> methods are invoked when the
left operand does not provide the appropriate method.</li>
</ul>
<p>It is intended that one set of <code class="docutils literal notranslate"><span class="pre">op</span></code> or <code class="docutils literal notranslate"><span class="pre">~op</span></code> is used for elementwise
operations, the other for objectwise operations, but it is not specified which
version of operators stands for elementwise or objectwise operations, leaving
the decision to applications.</p>
<p>The proposed implementation is to patch several files relating to the
tokenizer, parser, grammar and compiler to duplicate the functionality of
corresponding existing operators as necessary. All new semantics are to be
implemented in the classes that overload them.</p>
<p>The symbol <code class="docutils literal notranslate"><span class="pre">~</span></code> is already used in Python as the unary <em>bitwise not</em> operator.
Currently it is not allowed for binary operators. The new operators are
completely backward compatible.</p>
</section>
<section id="prototype-implementation">
<h2><a class="toc-backref" href="#prototype-implementation" role="doc-backlink">Prototype Implementation</a></h2>
<p>Greg Lielens implemented the infix <code class="docutils literal notranslate"><span class="pre">~op</span></code> as a patch against Python 2.0b1
source <a class="footnote-reference brackets" href="#id2" id="id1">[1]</a>.</p>
<p>To allow <code class="docutils literal notranslate"><span class="pre">~</span></code> to be part of binary operators, the tokenizer would treat <code class="docutils literal notranslate"><span class="pre">~+</span></code>
as one token. This means that currently valid expression <code class="docutils literal notranslate"><span class="pre">~+1</span></code> would be
tokenized as <code class="docutils literal notranslate"><span class="pre">~+</span></code> <code class="docutils literal notranslate"><span class="pre">1</span></code> instead of <code class="docutils literal notranslate"><span class="pre">~</span> <span class="pre">+</span> <span class="pre">1</span></code>. The parser would then treat <code class="docutils literal notranslate"><span class="pre">~+</span></code>
as composite of <code class="docutils literal notranslate"><span class="pre">~</span> <span class="pre">+</span></code>. The effect is invisible to applications.</p>
<p>Notes about current patch:</p>
<ul class="simple">
<li>It does not include <code class="docutils literal notranslate"><span class="pre">~op=</span></code> operators yet.</li>
<li>The <code class="docutils literal notranslate"><span class="pre">~op</span></code> behaves the same as <code class="docutils literal notranslate"><span class="pre">op</span></code> on lists, instead of raising
exceptions.</li>
</ul>
<p>These should be fixed when the final version of this proposal is ready.</p>
<ul>
<li>It reserves <code class="docutils literal notranslate"><span class="pre">xor</span></code> as an infix operator with the semantics equivalent to:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__xor__</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">b</span><span class="p">:</span> <span class="k">return</span> <span class="n">a</span>
<span class="k">elif</span> <span class="ow">not</span> <span class="n">a</span><span class="p">:</span> <span class="k">return</span> <span class="n">b</span>
<span class="k">else</span><span class="p">:</span> <span class="mi">0</span>
</pre></div>
</div>
</li>
</ul>
<p>This preserves true value as much as possible, otherwise preserve left hand
side value if possible.</p>
<p>This is done so that bitwise operators could be regarded as elementwise
logical operators in the future (see below).</p>
</section>
<section id="alternatives-to-adding-new-operators">
<h2><a class="toc-backref" href="#alternatives-to-adding-new-operators" role="doc-backlink">Alternatives to adding new operators</a></h2>
<p>The discussions on comp.lang.python and python-dev mailing list explored many
alternatives. Some of the leading alternatives are listed here, using the
multiplication operator as an example.</p>
<ol class="arabic">
<li>Use function <code class="docutils literal notranslate"><span class="pre">mul(a,b)</span></code>.<p>Advantage:</p>
<ul class="simple">
<li>No need for new operators.</li>
</ul>
<p>Disadvantage:</p>
<ul class="simple">
<li>Prefix forms are cumbersome for composite formulas.</li>
<li>Unfamiliar to the intended users.</li>
<li>Too verbose for the intended users.</li>
<li>Unable to use natural precedence rules.</li>
</ul>
</li>
<li>Use method call <code class="docutils literal notranslate"><span class="pre">a.mul(b)</span></code>.<p>Advantage:</p>
<ul class="simple">
<li>No need for new operators.</li>
</ul>
<p>Disadvantage:</p>
<ul class="simple">
<li>Asymmetric for both operands.</li>
<li>Unfamiliar to the intended users.</li>
<li>Too verbose for the intended users.</li>
<li>Unable to use natural precedence rules.</li>
</ul>
</li>
<li>Use <em>shadow classes</em>. For matrix class define a shadow array class
accessible through a method <code class="docutils literal notranslate"><span class="pre">.E</span></code>, so that for matrices <em>a</em> and <em>b</em>,
<code class="docutils literal notranslate"><span class="pre">a.E*b</span></code> would be a matrix object that is <code class="docutils literal notranslate"><span class="pre">elementwise_mul(a,b)</span></code>.<p>Likewise define a shadow matrix class for arrays accessible through a method
<code class="docutils literal notranslate"><span class="pre">.M</span></code> so that for arrays <em>a</em> and <em>b</em>, <code class="docutils literal notranslate"><span class="pre">a.M*b</span></code> would be an array that is
<code class="docutils literal notranslate"><span class="pre">matrixwise_mul(a,b)</span></code>.</p>
<p>Advantage:</p>
<ul class="simple">
<li>No need for new operators.</li>
<li>Benefits of infix operators with correct precedence rules.</li>
<li>Clean formulas in applications.</li>
</ul>
<p>Disadvantage:</p>
<ul class="simple">
<li>Hard to maintain in current Python because ordinary numbers cannot have
user defined class methods; i.e. <code class="docutils literal notranslate"><span class="pre">a.E*b</span></code> will fail if a is a pure
number.</li>
<li>Difficult to implement, as this will interfere with existing method calls,
like <code class="docutils literal notranslate"><span class="pre">.T</span></code> for transpose, etc.</li>
<li>Runtime overhead of object creation and method lookup.</li>
<li>The shadowing class cannot replace a true class, because it does not
return its own type. So there need to be a <code class="docutils literal notranslate"><span class="pre">M</span></code> class with shadow <code class="docutils literal notranslate"><span class="pre">E</span></code>
class, and an <code class="docutils literal notranslate"><span class="pre">E</span></code> class with shadow <code class="docutils literal notranslate"><span class="pre">M</span></code> class.</li>
<li>Unnatural to mathematicians.</li>
</ul>
</li>
<li>Implement matrixwise and elementwise classes with easy casting to the other
class. So matrixwise operations for arrays would be like <code class="docutils literal notranslate"><span class="pre">a.M*b.M</span></code> and
elementwise operations for matrices would be like <code class="docutils literal notranslate"><span class="pre">a.E*b.E</span></code>. For error
detection <code class="docutils literal notranslate"><span class="pre">a.E*b.M</span></code> would raise exceptions.<p>Advantage:</p>
<ul class="simple">
<li>No need for new operators.</li>
<li>Similar to infix notation with correct precedence rules.</li>
</ul>
<p>Disadvantage:</p>
<ul class="simple">
<li>Similar difficulty due to lack of user-methods for pure numbers.</li>
<li>Runtime overhead of object creation and method lookup.</li>
<li>More cluttered formulas.</li>
<li>Switching of flavor of objects to facilitate operators becomes persistent.
This introduces long range context dependencies in application code that
would be extremely hard to maintain.</li>
</ul>
</li>
<li>Using mini parser to parse formulas written in arbitrary extension placed in
quoted strings.<p>Advantage:</p>
<ul class="simple">
<li>Pure Python, without new operators</li>
</ul>
<p>Disadvantage:</p>
<ul class="simple">
<li>The actual syntax is within the quoted string, which does not resolve the
problem itself.</li>
<li>Introducing zones of special syntax.</li>
<li>Demanding on the mini-parser.</li>
</ul>
</li>
<li>Introducing a single operator, such as <code class="docutils literal notranslate"><span class="pre">&#64;</span></code>, for matrix multiplication.<p>Advantage:</p>
<ul class="simple">
<li>Introduces less operators</li>
</ul>
<p>Disadvantage:</p>
<ul class="simple">
<li>The distinctions for operators like <code class="docutils literal notranslate"><span class="pre">+</span></code> <code class="docutils literal notranslate"><span class="pre">-</span></code> <code class="docutils literal notranslate"><span class="pre">**</span></code> are equally
important. Their meaning in matrix or array-oriented packages would be
reversed (see below).</li>
<li>The new operator occupies a special character.</li>
<li>This does not work well with more general object-element issues.</li>
</ul>
</li>
</ol>
<p>Among these alternatives, the first and second are used in current applications
to some extent, but found inadequate. The third is the most favorite for
applications, but it will incur huge implementation complexity. The fourth
would make applications codes very context-sensitive and hard to maintain.
These two alternatives also share significant implementational difficulties due
to current type/class split. The fifth appears to create more problems than it
would solve. The sixth does not cover the same range of applications.</p>
</section>
<section id="alternative-forms-of-infix-operators">
<h2><a class="toc-backref" href="#alternative-forms-of-infix-operators" role="doc-backlink">Alternative forms of infix operators</a></h2>
<p>Two major forms and several minor variants of new infix operators were
discussed:</p>
<ul>
<li>Bracketed form:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">op</span><span class="p">)</span>
<span class="p">[</span><span class="n">op</span><span class="p">]</span>
<span class="p">{</span><span class="n">op</span><span class="p">}</span>
<span class="o">&lt;</span><span class="n">op</span><span class="o">&gt;</span>
<span class="p">:</span><span class="n">op</span><span class="p">:</span>
<span class="o">~</span><span class="n">op</span><span class="o">~</span>
<span class="o">%</span><span class="n">op</span><span class="o">%</span>
</pre></div>
</div>
</li>
<li>Meta character form:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">.</span><span class="n">op</span>
<span class="nd">@op</span>
<span class="o">~</span><span class="n">op</span>
</pre></div>
</div>
<p>Alternatively the meta character is put after the operator.</p>
</li>
<li>Less consistent variations of these themes. These are considered
unfavorably. For completeness some are listed here:<ul class="simple">
<li>Use <code class="docutils literal notranslate"><span class="pre">&#64;/</span></code> and <code class="docutils literal notranslate"><span class="pre">/&#64;</span></code> for left and right division</li>
<li>Use <code class="docutils literal notranslate"><span class="pre">[*]</span></code> and <code class="docutils literal notranslate"><span class="pre">(*)</span></code> for outer and inner products</li>
<li>Use a single operator <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> for multiplication.</li>
</ul>
</li>
<li>Use <code class="docutils literal notranslate"><span class="pre">__call__</span></code> to simulate multiplication:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">a</span><span class="p">)(</span><span class="n">b</span><span class="p">)</span>
</pre></div>
</div>
</li>
</ul>
<p>Criteria for choosing among the representations include:</p>
<ul class="simple">
<li>No syntactical ambiguities with existing operators.</li>
<li>Higher readability in actual formulas. This makes the bracketed forms
unfavorable. See examples below.</li>
<li>Visually similar to existing math operators.</li>
<li>Syntactically simple, without blocking possible future extensions.</li>
</ul>
<p>With these criteria the overall winner in bracket form appear to be <code class="docutils literal notranslate"><span class="pre">{op}</span></code>.
A clear winner in the meta character form is <code class="docutils literal notranslate"><span class="pre">~op</span></code>. Comparing these it
appears that <code class="docutils literal notranslate"><span class="pre">~op</span></code> is the favorite among them all.</p>
<p>Some analysis are as follows:</p>
<ul class="simple">
<li>The <code class="docutils literal notranslate"><span class="pre">.op</span></code> form is ambiguous: <code class="docutils literal notranslate"><span class="pre">1.+a</span></code> would be different from <code class="docutils literal notranslate"><span class="pre">1</span> <span class="pre">.+a</span></code>.</li>
<li>The bracket type operators are most favorable when standing alone, but
not in formulas, as they interfere with visual parsing of parentheses for
precedence and function argument. This is so for <code class="docutils literal notranslate"><span class="pre">(op)</span></code> and <code class="docutils literal notranslate"><span class="pre">[op]</span></code>, and
somewhat less so for <code class="docutils literal notranslate"><span class="pre">{op}</span></code> and <code class="docutils literal notranslate"><span class="pre">&lt;op&gt;</span></code>.</li>
<li>The <code class="docutils literal notranslate"><span class="pre">&lt;op&gt;</span></code> form has the potential to be confused with <code class="docutils literal notranslate"><span class="pre">&lt;</span></code> <code class="docutils literal notranslate"><span class="pre">&gt;</span></code> and <code class="docutils literal notranslate"><span class="pre">=</span></code>.</li>
<li>The <code class="docutils literal notranslate"><span class="pre">&#64;op</span></code> is not favored because <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> is visually heavy (dense, more like
a letter): <code class="docutils literal notranslate"><span class="pre">a&#64;+b</span></code> is more readily read as <code class="docutils literal notranslate"><span class="pre">a&#64;</span> <span class="pre">+</span> <span class="pre">b</span></code> than <code class="docutils literal notranslate"><span class="pre">a</span> <span class="pre">&#64;+</span> <span class="pre">b</span></code>.</li>
<li>For choosing meta-characters: Most of existing ASCII symbols have already
been used. The only three unused are <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> <code class="docutils literal notranslate"><span class="pre">$</span></code> <code class="docutils literal notranslate"><span class="pre">?</span></code>.</li>
</ul>
</section>
<section id="semantics-of-new-operators">
<h2><a class="toc-backref" href="#semantics-of-new-operators" role="doc-backlink">Semantics of new operators</a></h2>
<p>There are convincing arguments for using either set of operators as objectwise
or elementwise. Some of them are listed here:</p>
<ol class="arabic simple">
<li><code class="docutils literal notranslate"><span class="pre">op</span></code> for element, <code class="docutils literal notranslate"><span class="pre">~op</span></code> for object<ul class="simple">
<li>Consistent with current multiarray interface of Numeric package.</li>
<li>Consistent with some other languages.</li>
<li>Perception that elementwise operations are more natural.</li>
<li>Perception that elementwise operations are used more frequently</li>
</ul>
</li>
<li><code class="docutils literal notranslate"><span class="pre">op</span></code> for object, <code class="docutils literal notranslate"><span class="pre">~op</span></code> for element<ul class="simple">
<li>Consistent with current linear algebra interface of MatPy package.</li>
<li>Consistent with some other languages.</li>
<li>Perception that objectwise operations are more natural.</li>
<li>Perception that objectwise operations are used more frequently.</li>
<li>Consistent with the current behavior of operators on lists.</li>
<li>Allow <code class="docutils literal notranslate"><span class="pre">~</span></code> to be a general elementwise meta-character in future
extensions.</li>
</ul>
</li>
</ol>
<p>It is generally agreed upon that</p>
<ul class="simple">
<li>There is no absolute reason to favor one or the other.</li>
<li>It is easy to cast from one representation to another in a sizable chunk of
code, so the other flavor of operators is always minority.</li>
<li>There are other semantic differences that favor existence of array-oriented
and matrix-oriented packages, even if their operators are unified.</li>
<li>Whatever the decision is taken, codes using existing interfaces should not be
broken for a very long time.</li>
</ul>
<p>Therefore, not much is lost, and much flexibility retained, if the semantic
flavors of these two sets of operators are not dictated by the core language.
The application packages are responsible for making the most suitable choice.
This is already the case for NumPy and MatPy which use opposite semantics.
Adding new operators will not break this. See also observation after
subsection 2 in the Examples below.</p>
<p>The issue of numerical precision was raised, but if the semantics is left to
the applications, the actual precisions should also go there.</p>
</section>
<section id="examples">
<h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2>
<p>Following are examples of the actual formulas that will appear using various
operators or other representations described above.</p>
<ol class="arabic">
<li>The matrix inversion formula:<ul>
<li>Using <code class="docutils literal notranslate"><span class="pre">op</span></code> for object and <code class="docutils literal notranslate"><span class="pre">~op</span></code> for element:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">-</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">*</span> <span class="n">u</span> <span class="o">/</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="o">+</span> <span class="n">v</span><span class="o">/</span><span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="p">)</span> <span class="o">*</span> <span class="n">v</span> <span class="o">/</span> <span class="n">a</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">-</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">*</span> <span class="n">u</span> <span class="o">*</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="o">+</span> <span class="n">v</span><span class="o">*</span><span class="n">a</span><span class="o">.</span><span class="n">I</span><span class="o">*</span><span class="n">u</span><span class="p">)</span><span class="o">.</span><span class="n">I</span> <span class="o">*</span> <span class="n">v</span> <span class="o">*</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span>
</pre></div>
</div>
</li>
<li>Using <code class="docutils literal notranslate"><span class="pre">op</span></code> for element and <code class="docutils literal notranslate"><span class="pre">~op</span></code> for object:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">@-</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">@*</span> <span class="n">u</span> <span class="o">@/</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="o">@+</span> <span class="n">v</span><span class="o">@/</span><span class="n">a</span><span class="o">@*</span><span class="n">u</span><span class="p">)</span> <span class="o">@*</span> <span class="n">v</span> <span class="o">@/</span> <span class="n">a</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">~-</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">~*</span> <span class="n">u</span> <span class="o">~/</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="o">~+</span> <span class="n">v</span><span class="o">~/</span><span class="n">a</span><span class="o">~*</span><span class="n">u</span><span class="p">)</span> <span class="o">~*</span> <span class="n">v</span> <span class="o">~/</span> <span class="n">a</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="p">(</span><span class="o">-</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="o">*</span><span class="p">)</span> <span class="n">u</span> <span class="p">(</span><span class="o">/</span><span class="p">)</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="p">(</span><span class="o">+</span><span class="p">)</span> <span class="n">v</span><span class="p">(</span><span class="o">/</span><span class="p">)</span><span class="n">a</span><span class="p">(</span><span class="o">*</span><span class="p">)</span><span class="n">u</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="n">v</span> <span class="p">(</span><span class="o">/</span><span class="p">)</span> <span class="n">a</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="p">[</span><span class="o">-</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="o">*</span><span class="p">]</span> <span class="n">u</span> <span class="p">[</span><span class="o">/</span><span class="p">]</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="n">v</span><span class="p">[</span><span class="o">/</span><span class="p">]</span><span class="n">a</span><span class="p">[</span><span class="o">*</span><span class="p">]</span><span class="n">u</span><span class="p">)</span> <span class="p">[</span><span class="o">*</span><span class="p">]</span> <span class="n">v</span> <span class="p">[</span><span class="o">/</span><span class="p">]</span> <span class="n">a</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">&lt;-&gt;</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">&lt;*&gt;</span> <span class="n">u</span> <span class="o">&lt;/&gt;</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="o">&lt;+&gt;</span> <span class="n">v</span><span class="o">&lt;/&gt;</span><span class="n">a</span><span class="o">&lt;*&gt;</span><span class="n">u</span><span class="p">)</span> <span class="o">&lt;*&gt;</span> <span class="n">v</span> <span class="o">&lt;/&gt;</span> <span class="n">a</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="p">{</span><span class="o">-</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="o">*</span><span class="p">}</span> <span class="n">u</span> <span class="p">{</span><span class="o">/</span><span class="p">}</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="p">{</span><span class="o">+</span><span class="p">}</span> <span class="n">v</span><span class="p">{</span><span class="o">/</span><span class="p">}</span><span class="n">a</span><span class="p">{</span><span class="o">*</span><span class="p">}</span><span class="n">u</span><span class="p">)</span> <span class="p">{</span><span class="o">*</span><span class="p">}</span> <span class="n">v</span> <span class="p">{</span><span class="o">/</span><span class="p">}</span> <span class="n">a</span>
</pre></div>
</div>
</li>
</ul>
<p>Observation: For linear algebra using <code class="docutils literal notranslate"><span class="pre">op</span></code> for object is preferable.</p>
<p>Observation: The <code class="docutils literal notranslate"><span class="pre">~op</span></code> type operators look better than <code class="docutils literal notranslate"><span class="pre">(op)</span></code> type in
complicated formulas.</p>
<ul>
<li>using named operators:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="nd">@sub</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="nd">@mul</span> <span class="n">u</span> <span class="nd">@div</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="nd">@add</span> <span class="n">v</span> <span class="nd">@div</span> <span class="n">a</span> <span class="nd">@mul</span> <span class="n">u</span><span class="p">)</span> <span class="nd">@mul</span> <span class="n">v</span> <span class="nd">@div</span> <span class="n">a</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">~</span><span class="n">sub</span> <span class="n">a</span><span class="o">.</span><span class="n">I</span> <span class="o">~</span><span class="n">mul</span> <span class="n">u</span> <span class="o">~</span><span class="n">div</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">I</span> <span class="o">~</span><span class="n">add</span> <span class="n">v</span> <span class="o">~</span><span class="n">div</span> <span class="n">a</span> <span class="o">~</span><span class="n">mul</span> <span class="n">u</span><span class="p">)</span> <span class="o">~</span><span class="n">mul</span> <span class="n">v</span> <span class="o">~</span><span class="n">div</span> <span class="n">a</span>
</pre></div>
</div>
</li>
</ul>
<p>Observation: Named operators are not suitable for math formulas.</p>
</li>
<li>Plotting a 3d graph<ul>
<li>Using <code class="docutils literal notranslate"><span class="pre">op</span></code> for object and <code class="docutils literal notranslate"><span class="pre">~op</span></code> for element:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">z</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">x</span><span class="o">~**</span><span class="mi">2</span> <span class="o">~+</span> <span class="n">y</span><span class="o">~**</span><span class="mi">2</span><span class="p">);</span> <span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">z</span><span class="p">)</span>
</pre></div>
</div>
</li>
<li>Using op for element and ~op for object:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">z</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="n">y</span><span class="o">**</span><span class="mi">2</span><span class="p">);</span> <span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">z</span><span class="p">)</span>
</pre></div>
</div>
</li>
</ul>
<p>Observation: Elementwise operations with broadcasting allows much more
efficient implementation than MatLab.</p>
<p>Observation: It is useful to have two related classes with the semantics of
<code class="docutils literal notranslate"><span class="pre">op</span></code> and <code class="docutils literal notranslate"><span class="pre">~op</span></code> swapped. Using these the <code class="docutils literal notranslate"><span class="pre">~op</span></code> operators would only
need to appear in chunks of code where the other flavor dominates, while
maintaining consistent semantics of the code.</p>
</li>
<li>Using <code class="docutils literal notranslate"><span class="pre">+</span></code> and <code class="docutils literal notranslate"><span class="pre">-</span></code> with automatic broadcasting:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">=</span> <span class="n">b</span> <span class="o">-</span> <span class="n">c</span><span class="p">;</span> <span class="n">d</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">T</span><span class="o">*</span><span class="n">a</span>
</pre></div>
</div>
<p>Observation: This would silently produce hard-to-trace bugs if one of <em>b</em> or
<em>c</em> is row vector while the other is column vector.</p>
</li>
</ol>
</section>
<section id="miscellaneous-issues">
<h2><a class="toc-backref" href="#miscellaneous-issues" role="doc-backlink">Miscellaneous issues</a></h2>
<ul>
<li>Need for the <code class="docutils literal notranslate"><span class="pre">~+</span></code> <code class="docutils literal notranslate"><span class="pre">~-</span></code> operators. The objectwise <code class="docutils literal notranslate"><span class="pre">+</span></code> <code class="docutils literal notranslate"><span class="pre">-</span></code> are
important because they provide important sanity checks as per linear algebra.
The elementwise <code class="docutils literal notranslate"><span class="pre">+</span></code> <code class="docutils literal notranslate"><span class="pre">-</span></code> are important because they allow broadcasting
that are very efficient in applications.</li>
<li>Left division (solve). For matrix, <code class="docutils literal notranslate"><span class="pre">a*x</span></code> is not necessarily equal to
<code class="docutils literal notranslate"><span class="pre">x*a</span></code>. The solution of <code class="docutils literal notranslate"><span class="pre">a*x==b</span></code>, denoted <code class="docutils literal notranslate"><span class="pre">x=solve(a,b)</span></code>, is therefore
different from the solution of <code class="docutils literal notranslate"><span class="pre">x*a==b</span></code>, denoted <code class="docutils literal notranslate"><span class="pre">x=div(b,a)</span></code>. There are
discussions about finding a new symbol for solve. [Background: MatLab use
<code class="docutils literal notranslate"><span class="pre">b/a</span></code> for <code class="docutils literal notranslate"><span class="pre">div(b,a)</span></code> and <code class="docutils literal notranslate"><span class="pre">a\b</span></code> for <code class="docutils literal notranslate"><span class="pre">solve(a,b)</span></code>.]<p>It is recognized that Python provides a better solution without requiring a
new symbol: the <code class="docutils literal notranslate"><span class="pre">inverse</span></code> method <code class="docutils literal notranslate"><span class="pre">.I</span></code> can be made to be delayed so that
<code class="docutils literal notranslate"><span class="pre">a.I*b</span></code> and <code class="docutils literal notranslate"><span class="pre">b*a.I</span></code> are equivalent to Matlabs <code class="docutils literal notranslate"><span class="pre">a\b</span></code> and <code class="docutils literal notranslate"><span class="pre">b/a</span></code>. The
implementation is quite simple and the resulting application code clean.</p>
</li>
<li>Power operator. Pythons use of <code class="docutils literal notranslate"><span class="pre">a**b</span></code> as <code class="docutils literal notranslate"><span class="pre">pow(a,b)</span></code> has two perceived
disadvantages:<ul class="simple">
<li>Most mathematicians are more familiar with <code class="docutils literal notranslate"><span class="pre">a^b</span></code> for this purpose.</li>
<li>It results in long augmented assignment operator <code class="docutils literal notranslate"><span class="pre">~**=</span></code>.</li>
</ul>
<p>However, this issue is distinct from the main issue here.</p>
</li>
<li>Additional multiplication operators. Several forms of multiplications are
used in (multi-)linear algebra. Most can be seen as variations of
multiplication in linear algebra sense (such as Kronecker product). But two
forms appear to be more fundamental: outer product and inner product.
However, their specification includes indices, which can be either<ul class="simple">
<li>associated with the operator, or</li>
<li>associated with the objects.</li>
</ul>
<p>The latter (the Einstein notation) is used extensively on paper, and is also
the easier one to implement. By implementing a tensor-with-indices class, a
general form of multiplication would cover both outer and inner products, and
specialize to linear algebra multiplication as well. The index rule can be
defined as class methods, like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">i</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="o">-</span><span class="mi">1</span><span class="p">,</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">c</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># a_ijkl = b_ijmn c_lnkm</span>
</pre></div>
</div>
<p>Therefore, one objectwise multiplication is sufficient.</p>
</li>
<li>Bitwise operators.<ul class="simple">
<li>The proposed new math operators use the symbol ~ that is <em>bitwise not</em>
operator. This poses no compatibility problem but somewhat complicates
implementation.</li>
<li>The symbol <code class="docutils literal notranslate"><span class="pre">^</span></code> might be better used for <code class="docutils literal notranslate"><span class="pre">pow</span></code> than bitwise <code class="docutils literal notranslate"><span class="pre">xor</span></code>. But
this depends on the future of bitwise operators. It does not immediately
impact on the proposed math operator.</li>
<li>The symbol <code class="docutils literal notranslate"><span class="pre">|</span></code> was suggested to be used for matrix solve. But the new
solution of using delayed <code class="docutils literal notranslate"><span class="pre">.I</span></code> is better in several ways.</li>
<li>The current proposal fits in a larger and more general extension that will
remove the need for special bitwise operators. (See elementization below.)</li>
</ul>
</li>
<li>Alternative to special operator names used in definition,<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="s2">&quot;+&quot;</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">in</span> <span class="n">place</span> <span class="n">of</span> <span class="k">def</span> <span class="fm">__add__</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
</pre></div>
</div>
<p>This appears to require greater syntactical change, and would only be useful
when arbitrary additional operators are allowed.</p>
</li>
</ul>
</section>
<section id="impact-on-general-elementization">
<h2><a class="toc-backref" href="#impact-on-general-elementization" role="doc-backlink">Impact on general elementization</a></h2>
<p>The distinction between objectwise and elementwise operations are meaningful in
other contexts as well, where an object can be conceptually regarded as a
collection of elements. It is important that the current proposal does not
preclude possible future extensions.</p>
<p>One general future extension is to use <code class="docutils literal notranslate"><span class="pre">~</span></code> as a meta operator to <em>elementize</em>
a given operator. Several examples are listed here:</p>
<ol class="arabic">
<li>Bitwise operators. Currently Python assigns six operators to bitwise
operations: and (<code class="docutils literal notranslate"><span class="pre">&amp;</span></code>), or (<code class="docutils literal notranslate"><span class="pre">|</span></code>), xor (<code class="docutils literal notranslate"><span class="pre">^</span></code>), complement (<code class="docutils literal notranslate"><span class="pre">~</span></code>), left
shift (<code class="docutils literal notranslate"><span class="pre">&lt;&lt;</span></code>) and right shift (<code class="docutils literal notranslate"><span class="pre">&gt;&gt;</span></code>), with their own precedence levels.<p>Among them, the <code class="docutils literal notranslate"><span class="pre">&amp;</span></code> <code class="docutils literal notranslate"><span class="pre">|</span></code> <code class="docutils literal notranslate"><span class="pre">^</span></code> <code class="docutils literal notranslate"><span class="pre">~</span></code> operators can be regarded as
elementwise versions of lattice operators applied to integers regarded as
bit strings.:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="mi">5</span> <span class="ow">and</span> <span class="mi">6</span> <span class="c1"># 6</span>
<span class="mi">5</span> <span class="ow">or</span> <span class="mi">6</span> <span class="c1"># 5</span>
<span class="mi">5</span> <span class="o">~</span><span class="ow">and</span> <span class="mi">6</span> <span class="c1"># 4</span>
<span class="mi">5</span> <span class="o">~</span><span class="ow">or</span> <span class="mi">6</span> <span class="c1"># 7</span>
</pre></div>
</div>
<p>These can be regarded as general elementwise lattice operators, not
restricted to bits in integers.</p>
<p>In order to have named operators for <code class="docutils literal notranslate"><span class="pre">xor</span></code> <code class="docutils literal notranslate"><span class="pre">~xor</span></code>, it is necessary to
make <code class="docutils literal notranslate"><span class="pre">xor</span></code> a reserved word.</p>
</li>
<li>List arithmetics.:<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="o">+</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="c1"># [1, 2, 3, 4]</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="o">~+</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="c1"># [4, 6]</span>
<span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">]</span> <span class="o">*</span> <span class="mi">2</span> <span class="c1"># [&#39;a&#39;, &#39;b&#39;, &#39;a&#39;, &#39;b&#39;]</span>
<span class="s1">&#39;ab&#39;</span> <span class="o">*</span> <span class="mi">2</span> <span class="c1"># &#39;abab&#39;</span>
<span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">]</span> <span class="o">~*</span> <span class="mi">2</span> <span class="c1"># [&#39;aa&#39;, &#39;bb&#39;]</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="o">~*</span> <span class="mi">2</span> <span class="c1"># [2, 4]</span>
</pre></div>
</div>
<p>It is also consistent to Cartesian product:</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="o">*</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="c1"># [(1,3),(1,4),(2,3),(2,4)]</span>
</pre></div>
</div>
</li>
<li>List comprehension.:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">=</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="n">b</span> <span class="o">=</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="o">~</span><span class="n">f</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">)</span> <span class="c1"># [f(x,y) for x, y in zip(a,b)]</span>
<span class="o">~</span><span class="n">f</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">b</span><span class="p">)</span> <span class="c1"># [f(x,y) for x in a for y in b]</span>
<span class="n">a</span> <span class="o">~+</span> <span class="n">b</span> <span class="c1"># [x + y for x, y in zip(a,b)]</span>
</pre></div>
</div>
</li>
<li>Tuple generation (the zip function in Python 2.0):<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="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">]</span> <span class="c1"># ([1,2, 3], [4, 5, 6])</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="o">~</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">6</span><span class="p">]</span> <span class="c1"># [(1,4), (2, 5), (3,6)]</span>
</pre></div>
</div>
</li>
<li>Using <code class="docutils literal notranslate"><span class="pre">~</span></code> as generic elementwise meta-character to replace map:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">~</span><span class="n">f</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="c1"># map(f, a, b)</span>
<span class="o">~~</span><span class="n">f</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="c1"># map(lambda *x:map(f, *x), a, b)</span>
</pre></div>
</div>
<p>More generally,:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="o">~</span><span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">x</span><span class="p">):</span> <span class="k">return</span> <span class="nb">map</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">x</span><span class="p">)</span>
<span class="k">def</span> <span class="o">~~</span><span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">x</span><span class="p">):</span> <span class="k">return</span> <span class="nb">map</span><span class="p">(</span><span class="o">~</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">x</span><span class="p">)</span>
<span class="o">...</span>
</pre></div>
</div>
</li>
<li>Elementwise format operator (with broadcasting):<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">=</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="nb">print</span> <span class="p">[</span><span class="s2">&quot;</span><span class="si">%5d</span><span class="s2"> &quot;</span><span class="p">]</span> <span class="o">~%</span> <span class="n">a</span>
<span class="n">a</span> <span class="o">=</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="nb">print</span> <span class="p">[</span><span class="s2">&quot;</span><span class="si">%5d</span><span class="s2"> &quot;</span><span class="p">]</span> <span class="o">~~%</span> <span class="n">a</span>
</pre></div>
</div>
</li>
<li>Rich comparison:<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="o">~&lt;</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span> <span class="c1"># [1, 0, 0]</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="o">~==</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span> <span class="c1"># [0, 1, 0]</span>
</pre></div>
</div>
</li>
<li>Rich indexing:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">d</span><span class="p">]</span> <span class="o">~</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">1</span><span class="p">]</span> <span class="c1"># [c, d, b]</span>
</pre></div>
</div>
</li>
<li>Tuple flattening:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">=</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="n">b</span> <span class="o">=</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="n">f</span><span class="p">(</span><span class="o">~</span><span class="n">a</span><span class="p">,</span> <span class="o">~</span><span class="n">b</span><span class="p">)</span> <span class="c1"># f(1,2,3,4)</span>
</pre></div>
</div>
</li>
<li>Copy operator:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">~=</span> <span class="n">b</span> <span class="c1"># a = b.copy()</span>
</pre></div>
</div>
</li>
</ol>
<blockquote>
<div>There can be specific levels of deep copy:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">~~=</span> <span class="n">b</span> <span class="c1"># a = b.copy(2)</span>
</pre></div>
</div>
</div></blockquote>
<section id="notes">
<h3><a class="toc-backref" href="#notes" role="doc-backlink">Notes</a></h3>
<ol class="arabic simple">
<li>There are probably many other similar situations. This general approach
seems well suited for most of them, in place of several separated extensions
for each of them (parallel and cross iteration, list comprehension, rich
comparison, etc).</li>
<li>The semantics of <em>elementwise</em> depends on applications. For example, an
element of matrix is two levels down from the list-of-list point of view.
This requires more fundamental change than the current proposal. In any
case, the current proposal will not negatively impact on future
possibilities of this nature.</li>
</ol>
<p>Note that this section describes a type of future extensions that is consistent
with current proposal, but may present additional compatibility or other
problems. They are not tied to the current proposal.</p>
</section>
</section>
<section id="impact-on-named-operators">
<h2><a class="toc-backref" href="#impact-on-named-operators" role="doc-backlink">Impact on named operators</a></h2>
<p>The discussions made it generally clear that infix operators is a scarce
resource in Python, not only in numerical computation, but in other fields as
well. Several proposals and ideas were put forward that would allow infix
operators be introduced in ways similar to named functions. We show here that
the current extension does not negatively impact on future extensions in this
regard.</p>
<ol class="arabic">
<li>Named infix operators.<p>Choose a meta character, say <code class="docutils literal notranslate"><span class="pre">&#64;</span></code>, so that for any identifier <code class="docutils literal notranslate"><span class="pre">opname</span></code>,
the combination <code class="docutils literal notranslate"><span class="pre">&#64;opname</span></code> would be a binary infix operator, and:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="nd">@opname</span> <span class="n">b</span> <span class="o">==</span> <span class="n">opname</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">)</span>
</pre></div>
</div>
<p>Other representations mentioned include:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">.</span><span class="n">name</span> <span class="o">~</span><span class="n">name</span><span class="o">~</span> <span class="p">:</span><span class="n">name</span><span class="p">:</span> <span class="p">(</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="o">%</span><span class="n">name</span><span class="o">%</span>
</pre></div>
</div>
<p>and similar variations. The pure bracket based operators cannot be used
this way.</p>
<p>This requires a change in the parser to recognize <code class="docutils literal notranslate"><span class="pre">&#64;opname</span></code>, and parse it
into the same structure as a function call. The precedence of all these
operators would have to be fixed at one level, so the implementation would
be different from additional math operators which keep the precedence of
existing math operators.</p>
<p>The current proposed extension do not limit possible future extensions of
such form in any way.</p>
</li>
<li>More general symbolic operators.<p>One additional form of future extension is to use meta character and
operator symbols (symbols that cannot be used in syntactical structures
other than operators). Suppose <code class="docutils literal notranslate"><span class="pre">&#64;</span></code> is the meta character. Then:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">@+</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">@@+</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">@+-</span> <span class="n">b</span>
</pre></div>
</div>
<p>would all be operators with a hierarchy of precedence, defined by:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="s2">&quot;+&quot;</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">def</span> <span class="s2">&quot;@+&quot;</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">def</span> <span class="s2">&quot;@@+&quot;</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">def</span> <span class="s2">&quot;@+-&quot;</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
</pre></div>
</div>
<p>One advantage compared with named operators is greater flexibility for
precedences based on either the meta character or the ordinary operator
symbols. This also allows operator composition. The disadvantage is that
they are more like <em>line noise</em>. In any case the current proposal does not
impact its future possibility.</p>
<p>These kinds of future extensions may not be necessary when Unicode becomes
generally available.</p>
<p>Note that this section discusses compatibility of the proposed extension
with possible future extensions. The desirability or compatibility of these
other extensions themselves are specifically not considered here.</p>
</li>
</ol>
</section>
<section id="credits-and-archives">
<h2><a class="toc-backref" href="#credits-and-archives" role="doc-backlink">Credits and archives</a></h2>
<p>The discussions mostly happened in July to August of 2000 on news group
comp.lang.python and the mailing list python-dev. There are altogether several
hundred postings, most can be retrieved from these two pages (and searching
word “operator”):</p>
<blockquote>
<div><a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/">http://www.python.org/pipermail/python-list/2000-July/</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/">http://www.python.org/pipermail/python-list/2000-August/</a></div></blockquote>
<p>The names of contributors are too numerous to mention here, suffice to say that
a large proportion of ideas discussed here are not our own.</p>
<p>Several key postings (from our point of view) that may help to navigate the
discussions include:</p>
<blockquote>
<div><a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/108893.html">http://www.python.org/pipermail/python-list/2000-July/108893.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/108777.html">http://www.python.org/pipermail/python-list/2000-July/108777.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/108848.html">http://www.python.org/pipermail/python-list/2000-July/108848.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109237.html">http://www.python.org/pipermail/python-list/2000-July/109237.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109250.html">http://www.python.org/pipermail/python-list/2000-July/109250.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109310.html">http://www.python.org/pipermail/python-list/2000-July/109310.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109448.html">http://www.python.org/pipermail/python-list/2000-July/109448.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109491.html">http://www.python.org/pipermail/python-list/2000-July/109491.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109537.html">http://www.python.org/pipermail/python-list/2000-July/109537.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109607.html">http://www.python.org/pipermail/python-list/2000-July/109607.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109709.html">http://www.python.org/pipermail/python-list/2000-July/109709.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109804.html">http://www.python.org/pipermail/python-list/2000-July/109804.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/109857.html">http://www.python.org/pipermail/python-list/2000-July/109857.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/110061.html">http://www.python.org/pipermail/python-list/2000-July/110061.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-July/110208.html">http://www.python.org/pipermail/python-list/2000-July/110208.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/111427.html">http://www.python.org/pipermail/python-list/2000-August/111427.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/111558.html">http://www.python.org/pipermail/python-list/2000-August/111558.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/112551.html">http://www.python.org/pipermail/python-list/2000-August/112551.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/112606.html">http://www.python.org/pipermail/python-list/2000-August/112606.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/112758.html">http://www.python.org/pipermail/python-list/2000-August/112758.html</a><p><a class="reference external" href="http://www.python.org/pipermail/python-dev/2000-July/013243.html">http://www.python.org/pipermail/python-dev/2000-July/013243.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-dev/2000-July/013364.html">http://www.python.org/pipermail/python-dev/2000-July/013364.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-dev/2000-August/014940.html">http://www.python.org/pipermail/python-dev/2000-August/014940.html</a></p>
</div></blockquote>
<p>These are earlier drafts of this PEP:</p>
<blockquote>
<div><a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/111785.html">http://www.python.org/pipermail/python-list/2000-August/111785.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-list/2000-August/112529.html">http://www.python.org/pipermail/python-list/2000-August/112529.html</a>
<a class="reference external" href="http://www.python.org/pipermail/python-dev/2000-August/014906.html">http://www.python.org/pipermail/python-dev/2000-August/014906.html</a></div></blockquote>
<p>There is an alternative PEP (officially, <a class="pep reference internal" href="../pep-0211/" title="PEP 211 Adding A New Outer Product Operator">PEP 211</a>) by Greg Wilson, titled
“Adding New Linear Algebra Operators to Python”.</p>
<p>Its first (and current) version is at:</p>
<blockquote>
<div><a class="reference external" href="http://www.python.org/pipermail/python-dev/2000-August/014876.html">http://www.python.org/pipermail/python-dev/2000-August/014876.html</a>
<a class="pep reference internal" href="../pep-0211/" title="PEP 211 Adding A New Outer Product Operator">PEP 211</a></div></blockquote>
</section>
<section id="additional-references">
<h2><a class="toc-backref" href="#additional-references" role="doc-backlink">Additional References</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id2" role="doc-footnote">
<dt class="label" id="id2">[<a href="#id1">1</a>]</dt>
<dd><a class="reference external" href="http://MatPy.sourceforge.net/Misc/index.html">http://MatPy.sourceforge.net/Misc/index.html</a></aside>
</aside>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0225.rst">https://github.com/python/peps/blob/main/peps/pep-0225.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0225.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="#background">Background</a></li>
<li><a class="reference internal" href="#proposed-extension">Proposed extension</a></li>
<li><a class="reference internal" href="#prototype-implementation">Prototype Implementation</a></li>
<li><a class="reference internal" href="#alternatives-to-adding-new-operators">Alternatives to adding new operators</a></li>
<li><a class="reference internal" href="#alternative-forms-of-infix-operators">Alternative forms of infix operators</a></li>
<li><a class="reference internal" href="#semantics-of-new-operators">Semantics of new operators</a></li>
<li><a class="reference internal" href="#examples">Examples</a></li>
<li><a class="reference internal" href="#miscellaneous-issues">Miscellaneous issues</a></li>
<li><a class="reference internal" href="#impact-on-general-elementization">Impact on general elementization</a><ul>
<li><a class="reference internal" href="#notes">Notes</a></li>
</ul>
</li>
<li><a class="reference internal" href="#impact-on-named-operators">Impact on named operators</a></li>
<li><a class="reference internal" href="#credits-and-archives">Credits and archives</a></li>
<li><a class="reference internal" href="#additional-references">Additional References</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0225.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>