CINXE.COM
PEP 322 – Reverse Iteration | peps.python.org
<!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 322 – Reverse Iteration | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0322/"> <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 322 – Reverse Iteration | peps.python.org'> <meta property="og:description" content="This proposal is to add a builtin function to support reverse iteration over sequences."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0322/"> <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 proposal is to add a builtin function to support reverse iteration over sequences."> <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 322</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 322 – Reverse Iteration</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Raymond Hettinger <python at rcn.com></dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Created<span class="colon">:</span></dt> <dd class="field-even">24-Sep-2003</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">2.4</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even">24-Sep-2003</dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#proposal">Proposal</a></li> <li><a class="reference internal" href="#bdfl-pronouncement">BDFL Pronouncement</a></li> <li><a class="reference internal" href="#alternative-method-names">Alternative Method Names</a></li> <li><a class="reference internal" href="#discussion">Discussion</a></li> <li><a class="reference internal" href="#real-world-use-cases">Real World Use Cases</a></li> <li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This proposal is to add a builtin function to support reverse iteration over sequences.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>For indexable objects, current approaches for reverse iteration are error prone, unnatural, and not especially readable:</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">xrange</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span> <span class="nb">print</span> <span class="n">seqn</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </pre></div> </div> <p>One other current approach involves reversing a list before iterating over it. That technique wastes computer cycles, memory, and lines of code:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">rseqn</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">seqn</span><span class="p">)</span> <span class="n">rseqn</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span> <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">rseqn</span><span class="p">:</span> <span class="nb">print</span> <span class="n">value</span> </pre></div> </div> <p>Extended slicing is a third approach that minimizes the code overhead but does nothing for memory efficiency, beauty, or clarity.</p> <p>Reverse iteration is much less common than forward iteration, but it does arise regularly in practice. See <a class="reference internal" href="#real-world-use-cases">Real World Use Cases</a> below.</p> </section> <section id="proposal"> <h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2> <p>Add a builtin function called <em>reversed()</em> that makes a reverse iterator over sequence objects that support __getitem__() and __len__().</p> <p>The above examples then simplify 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="nb">reversed</span><span class="p">(</span><span class="n">xrange</span><span class="p">(</span><span class="n">n</span><span class="p">)):</span> <span class="nb">print</span> <span class="n">seqn</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </pre></div> </div> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">elem</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">seqn</span><span class="p">):</span> <span class="nb">print</span> <span class="n">elem</span> </pre></div> </div> <p>The core idea is that the clearest, least error-prone way of specifying reverse iteration is to specify it in a forward direction and then say <em>reversed</em>.</p> <p>The implementation could be as simple as:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">reversed</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s1">'keys'</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"mappings do not support reverse iteration"</span><span class="p">)</span> <span class="n">i</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">while</span> <span class="n">i</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span> <span class="n">i</span> <span class="o">-=</span> <span class="mi">1</span> <span class="k">yield</span> <span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </pre></div> </div> <p>No language syntax changes are needed. The proposal is fully backwards compatible.</p> <p>A C implementation and unit tests are at: <a class="reference external" href="https://bugs.python.org/issue834422">https://bugs.python.org/issue834422</a></p> </section> <section id="bdfl-pronouncement"> <h2><a class="toc-backref" href="#bdfl-pronouncement" role="doc-backlink">BDFL Pronouncement</a></h2> <p>This PEP has been conditionally accepted for Py2.4. The condition means that if the function is found to be useless, it can be removed before Py2.4b1.</p> </section> <section id="alternative-method-names"> <h2><a class="toc-backref" href="#alternative-method-names" role="doc-backlink">Alternative Method Names</a></h2> <ul class="simple"> <li><em>reviter</em> – Jeremy Fincher’s suggestion matches use of iter()</li> <li><em>ireverse</em> – uses the itertools naming convention</li> <li><em>inreverse</em> – no one seems to like this one except me</li> </ul> <p>The name <em>reverse</em> is not a candidate because it duplicates the name of the list.reverse() which mutates the underlying list.</p> </section> <section id="discussion"> <h2><a class="toc-backref" href="#discussion" role="doc-backlink">Discussion</a></h2> <p>The case against adoption of the PEP is a desire to keep the number of builtin functions small. This needs to weighed against the simplicity and convenience of having it as builtin instead of being tucked away in some other namespace.</p> </section> <section id="real-world-use-cases"> <h2><a class="toc-backref" href="#real-world-use-cases" role="doc-backlink">Real World Use Cases</a></h2> <p>Here are some instances of reverse iteration taken from the standard library and comments on why reverse iteration was necessary:</p> <ul> <li>atexit.exit_handlers() uses:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">while</span> <span class="n">_exithandlers</span><span class="p">:</span> <span class="n">func</span><span class="p">,</span> <span class="n">targs</span><span class="p">,</span> <span class="n">kargs</span> <span class="o">=</span> <span class="n">_exithandlers</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span> <span class="o">.</span> <span class="o">.</span> <span class="o">.</span> </pre></div> </div> <p>In this application popping is required, so the new function would not help.</p> </li> <li>heapq.heapify() uses <code class="docutils literal notranslate"><span class="pre">for</span> <span class="pre">i</span> <span class="pre">in</span> <span class="pre">xrange(n//2</span> <span class="pre">-</span> <span class="pre">1,</span> <span class="pre">-1,</span> <span class="pre">-1)</span></code> because higher-level orderings are more easily formed from pairs of lower-level orderings. A forward version of this algorithm is possible; however, that would complicate the rest of the heap code which iterates over the underlying list in the opposite direction. The replacement code <code class="docutils literal notranslate"><span class="pre">for</span> <span class="pre">i</span> <span class="pre">in</span> <span class="pre">reversed(xrange(n//2))</span></code> makes clear the range covered and how many iterations it takes.</li> <li>mhlib.test() uses:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>testfolders.reverse(); for t in testfolders: do('mh.deletefolder(%s)' % `t`) </pre></div> </div> <p>The need for reverse iteration arises because the tail of the underlying list is altered during iteration.</p> </li> <li>platform._dist_try_harder() uses <code class="docutils literal notranslate"><span class="pre">for</span> <span class="pre">n</span> <span class="pre">in</span> <span class="pre">range(len(verfiles)-1,-1,-1)</span></code> because the loop deletes selected elements from <em>verfiles</em> but needs to leave the rest of the list intact for further iteration.</li> <li>random.shuffle() uses <code class="docutils literal notranslate"><span class="pre">for</span> <span class="pre">i</span> <span class="pre">in</span> <span class="pre">xrange(len(x)-1,</span> <span class="pre">0,</span> <span class="pre">-1)</span></code> because the algorithm is most easily understood as randomly selecting elements from an ever diminishing pool. In fact, the algorithm can be run in a forward direction but is less intuitive and rarely presented that way in literature. The replacement code <code class="docutils literal notranslate"><span class="pre">for</span> <span class="pre">i</span> <span class="pre">in</span> <span class="pre">reversed(xrange(1,</span> <span class="pre">len(x)))</span></code> is much easier to verify visually.</li> <li>rfc822.Message.__delitem__() uses:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">list</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">:</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> </pre></div> </div> <p>The need for reverse iteration arises because the tail of the underlying list is altered during iteration.</p> </li> </ul> </section> <section id="rejected-alternatives"> <h2><a class="toc-backref" href="#rejected-alternatives" role="doc-backlink">Rejected Alternatives</a></h2> <p>Several variants were submitted that attempted to apply <em>reversed()</em> to all iterables by running the iterable to completion, saving the results, and then returning a reverse iterator over the results. While satisfying some notions of full generality, running the input to the end is contrary to the purpose of using iterators in the first place. Also, a small disaster ensues if the underlying iterator is infinite.</p> <p>Putting the function in another module or attaching it to a type object is not being considered. Like its cousins, <em>zip()</em> and <em>enumerate()</em>, the function needs to be directly accessible in daily programming. Each solves a basic looping problem: lock-step iteration, loop counting, and reverse iteration. Requiring some form of dotted access would interfere with their simplicity, daily utility, and accessibility. They are core looping constructs, independent of any one application domain.</p> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0322.rst">https://github.com/python/peps/blob/main/peps/pep-0322.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0322.rst">2025-02-01 08:59:27 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#proposal">Proposal</a></li> <li><a class="reference internal" href="#bdfl-pronouncement">BDFL Pronouncement</a></li> <li><a class="reference internal" href="#alternative-method-names">Alternative Method Names</a></li> <li><a class="reference internal" href="#discussion">Discussion</a></li> <li><a class="reference internal" href="#real-world-use-cases">Real World Use Cases</a></li> <li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0322.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>