306 lines
22 KiB
HTML
306 lines
22 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 211 – Adding A New Outer Product Operator | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0211/">
|
||
<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 211 – Adding A New Outer Product Operator | peps.python.org'>
|
||
<meta property="og:description" content="This PEP describes a proposal to define @ (pronounced “across”) as a new outer product operator in Python 2.2. When applied to sequences (or other iterable objects), this operator will combine their iterators, so that:">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0211/">
|
||
<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 a proposal to define @ (pronounced “across”) as a new outer product operator in Python 2.2. When applied to sequences (or other iterable objects), this operator will combine their iterators, so that:">
|
||
<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 211</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 211 – Adding A New Outer Product Operator</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Greg Wilson <gvwilson at ddj.com></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">15-Jul-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="#iterators">Iterators</a></li>
|
||
<li><a class="reference internal" href="#discussion">Discussion</a></li>
|
||
<li><a class="reference internal" href="#alternatives">Alternatives</a></li>
|
||
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<div class="pep-banner sticky-banner deprecated rejected admonition warning">
|
||
<p class="admonition-title">Warning</p>
|
||
<p>This PEP has been rejected.</p>
|
||
<p class="close-button">×</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>
|
||
<p></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 define <code class="docutils literal notranslate"><span class="pre">@</span></code> (pronounced “across”)
|
||
as a new outer product operator in Python 2.2. When applied to
|
||
sequences (or other iterable objects), this operator will combine
|
||
their iterators, so that:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span> <span class="ow">in</span> <span class="n">S</span> <span class="o">@</span> <span class="n">T</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>will be equivalent to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">S</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">T</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Classes will be able to overload this operator using the special
|
||
methods <code class="docutils literal notranslate"><span class="pre">__across__</span></code>, <code class="docutils literal notranslate"><span class="pre">__racross__</span></code>, and <code class="docutils literal notranslate"><span class="pre">__iacross__</span></code>. In
|
||
particular, the new Numeric module (<a class="pep reference internal" href="../pep-0209/" title="PEP 209 – Multi-dimensional Arrays">PEP 209</a>) will overload this
|
||
operator for multi-dimensional arrays to implement matrix
|
||
multiplication.</p>
|
||
</section>
|
||
<section id="background">
|
||
<h2><a class="toc-backref" href="#background" role="doc-backlink">Background</a></h2>
|
||
<p>Number-crunching is now just a small part of computing, but many
|
||
programmers — including many Python users — still need to
|
||
express complex mathematical operations in code. Most numerical
|
||
languages, such as APL, Fortran-90, MATLAB, IDL, and Mathematica,
|
||
therefore provide two forms of the common arithmetic operators.
|
||
One form works element-by-element, e.g. multiplies corresponding
|
||
elements of its matrix arguments. The other implements the
|
||
“mathematical” definition of that operation, e.g. performs
|
||
row-column matrix multiplication.</p>
|
||
<p>Zhu and Lielens have <a class="pep reference internal" href="../pep-0225/" title="PEP 225 – Elementwise/Objectwise Operators">proposed</a> doubling up Python’s operators in
|
||
this way. Their proposal would create six new binary infix
|
||
operators, and six new in-place operators.</p>
|
||
<p>The original version of this proposal was much more conservative.
|
||
The author consulted the developers of GNU Octave <a class="footnote-reference brackets" href="#id5" id="id1">[1]</a>, an open
|
||
source clone of MATLAB. Its developers agreed that providing an
|
||
infix operator for matrix multiplication was important: numerical
|
||
programmers really do care whether they have to write <code class="docutils literal notranslate"><span class="pre">mmul(A,B)</span></code>
|
||
instead of <code class="docutils literal notranslate"><span class="pre">A</span> <span class="pre">op</span> <span class="pre">B</span></code>.</p>
|
||
<p>On the other hand, when asked how important it was to have infix
|
||
operators for matrix solution and other operations, Prof. James
|
||
Rawlings replied <a class="footnote-reference brackets" href="#id6" id="id2">[2]</a>:</p>
|
||
<blockquote>
|
||
<div>I DON’T think it’s a must have, and I do a lot of matrix
|
||
inversion. I cannot remember if its <code class="docutils literal notranslate"><span class="pre">A\b</span></code> or <code class="docutils literal notranslate"><span class="pre">b\A</span></code> so I always
|
||
write <code class="docutils literal notranslate"><span class="pre">inv(A)*b</span></code> instead. I recommend dropping <code class="docutils literal notranslate"><span class="pre">\</span></code>.</div></blockquote>
|
||
<p>Based on this discussion, and feedback from students at the US
|
||
national laboratories and elsewhere, we recommended adding only
|
||
one new operator, for matrix multiplication, to Python.</p>
|
||
</section>
|
||
<section id="iterators">
|
||
<h2><a class="toc-backref" href="#iterators" role="doc-backlink">Iterators</a></h2>
|
||
<p>The planned addition of iterators to Python 2.2 opens up a broader
|
||
scope for this proposal. As part of the discussion of <a class="pep reference internal" href="../pep-0201/" title="PEP 201 – Lockstep Iteration">PEP 201</a>,
|
||
Lockstep Iteration, the author of this proposal conducted an
|
||
informal usability experiment <a class="footnote-reference brackets" href="#id7" id="id3">[3]</a>. The results showed that users
|
||
are psychologically receptive to “cross-product” loop syntax. For
|
||
example, most users expected:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">S</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">]</span>
|
||
<span class="n">T</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="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">S</span><span class="p">;</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">T</span><span class="p">:</span>
|
||
<span class="nb">print</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="p">,</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>to print <code class="docutils literal notranslate"><span class="pre">11</span> <span class="pre">12</span> <span class="pre">13</span> <span class="pre">21</span> <span class="pre">22</span> <span class="pre">23</span> <span class="pre">31</span> <span class="pre">32</span> <span class="pre">33</span></code>. We believe that users will
|
||
have the same reaction to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">for</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="ow">in</span> <span class="n">S</span> <span class="o">@</span> <span class="n">T</span><span class="p">:</span>
|
||
<span class="nb">print</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>i.e. that they will naturally interpret this as a tidy way to
|
||
write loop nests.</p>
|
||
<p>This is where iterators come in. Actually constructing the
|
||
cross-product of two (or more) sequences before executing the loop
|
||
would be very expensive. On the other hand, <code class="docutils literal notranslate"><span class="pre">@</span></code> could be defined
|
||
to get its arguments’ iterators, and then create an outer iterator
|
||
which returns tuples of the values returned by the inner
|
||
iterators.</p>
|
||
</section>
|
||
<section id="discussion">
|
||
<h2><a class="toc-backref" href="#discussion" role="doc-backlink">Discussion</a></h2>
|
||
<ol class="arabic">
|
||
<li>Adding a named function “across” would have less impact on
|
||
Python than a new infix operator. However, this would not make
|
||
Python more appealing to numerical programmers, who really do
|
||
care whether they can write matrix multiplication using an
|
||
operator, or whether they have to write it as a function call.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">@</span></code> would have be chainable in the same way as comparison
|
||
operators, i.e.:<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="o">@</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>would have to return <code class="docutils literal notranslate"><span class="pre">(1,</span> <span class="pre">3,</span> <span class="pre">5)</span> <span class="pre">...</span> <span class="pre">(2,</span> <span class="pre">4,</span> <span class="pre">6)</span></code>, and <em>not</em>
|
||
<code class="docutils literal notranslate"><span class="pre">((1,</span> <span class="pre">3),</span> <span class="pre">5)</span> <span class="pre">...</span> <span class="pre">((2,</span> <span class="pre">4),</span> <span class="pre">6)</span></code>. This should not require special
|
||
support from the parser, as the outer iterator created by the
|
||
first <code class="docutils literal notranslate"><span class="pre">@</span></code> could easily be taught how to combine itself with
|
||
ordinary iterators.</p>
|
||
</li>
|
||
<li>There would have to be some way to distinguish restartable
|
||
iterators from ones that couldn’t be restarted. For example,
|
||
if <code class="docutils literal notranslate"><span class="pre">S</span></code> is an input stream (e.g. a file), and <code class="docutils literal notranslate"><span class="pre">L</span></code> is a list, then <code class="docutils literal notranslate"><span class="pre">S</span>
|
||
<span class="pre">@</span> <span class="pre">L</span></code> is straightforward, but <code class="docutils literal notranslate"><span class="pre">L</span> <span class="pre">@</span> <span class="pre">S</span></code> is not, since iteration
|
||
through the stream cannot be repeated. This could be treated
|
||
as an error, or by having the outer iterator detect
|
||
non-restartable inner iterators and cache their values.</li>
|
||
<li>Whiteboard testing of this proposal in front of three novice
|
||
Python users (all of them experienced programmers) indicates
|
||
that users will expect:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="s2">"ab"</span> <span class="o">@</span> <span class="s2">"cd"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>to return four strings, not four tuples of pairs of
|
||
characters. Opinion was divided on what:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="s2">"a"</span><span class="p">,</span> <span class="s2">"b"</span><span class="p">)</span> <span class="o">@</span> <span class="s2">"cd"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>ought to return…</p>
|
||
</li>
|
||
</ol>
|
||
</section>
|
||
<section id="alternatives">
|
||
<h2><a class="toc-backref" href="#alternatives" role="doc-backlink">Alternatives</a></h2>
|
||
<ol class="arabic">
|
||
<li>Do nothing — keep Python simple.<p>This is always the default choice.</p>
|
||
</li>
|
||
<li>Add a named function instead of an operator.<p>Python is not primarily a numerical language; it may not be worth
|
||
complexifying it for this special case. However, support for real
|
||
matrix multiplication <em>is</em> frequently requested, and the proposed
|
||
semantics for <code class="docutils literal notranslate"><span class="pre">@</span></code> for built-in sequence types would simplify
|
||
expression of a very common idiom (nested loops).</p>
|
||
</li>
|
||
<li>Introduce prefixed forms of all existing operators, such as
|
||
<code class="docutils literal notranslate"><span class="pre">~*</span></code> and <code class="docutils literal notranslate"><span class="pre">~+</span></code>, as proposed in <a class="pep reference internal" href="../pep-0225/" title="PEP 225 – Elementwise/Objectwise Operators">PEP 225</a>.<p>Our objections to this are that there isn’t enough demand to
|
||
justify the additional complexity (see Rawlings’ comments <a class="footnote-reference brackets" href="#id6" id="id4">[2]</a>),
|
||
and that the proposed syntax fails the “low toner” readability
|
||
test.</p>
|
||
</li>
|
||
</ol>
|
||
</section>
|
||
<section id="acknowledgments">
|
||
<h2><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h2>
|
||
<p>I am grateful to Huaiyu Zhu for initiating this discussion, and to
|
||
James Rawlings and students in various Python courses for their
|
||
discussions of what numerical programmers really care about.</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="id5" role="doc-footnote">
|
||
<dt class="label" id="id5">[<a href="#id1">1</a>]</dt>
|
||
<dd><a class="reference external" href="http://bevo.che.wisc.edu/octave/">http://bevo.che.wisc.edu/octave/</a></aside>
|
||
<aside class="footnote brackets" id="id6" role="doc-footnote">
|
||
<dt class="label" id="id6">[2]<em> (<a href='#id2'>1</a>, <a href='#id4'>2</a>) </em></dt>
|
||
<dd><a class="reference external" href="http://www.egroups.com/message/python-numeric/4">http://www.egroups.com/message/python-numeric/4</a></aside>
|
||
<aside class="footnote brackets" id="id7" role="doc-footnote">
|
||
<dt class="label" id="id7">[<a href="#id3">3</a>]</dt>
|
||
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2000-July/006427.html">https://mail.python.org/pipermail/python-dev/2000-July/006427.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-0211.rst">https://github.com/python/peps/blob/main/peps/pep-0211.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0211.rst">2024-04-14 20:08:31 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="#iterators">Iterators</a></li>
|
||
<li><a class="reference internal" href="#discussion">Discussion</a></li>
|
||
<li><a class="reference internal" href="#alternatives">Alternatives</a></li>
|
||
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</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-0211.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> |