python-peps/pep-0469/index.html

495 lines
42 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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 469 Migration of dict iteration code to Python 3 | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0469/">
<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 469 Migration of dict iteration code to Python 3 | peps.python.org'>
<meta property="og:description" content="For Python 3, PEP 3106 changed the design of the dict builtin and the mapping API in general to replace the separate list based and iterator based APIs in Python 2 with a merged, memory efficient set and multiset view based API. This new style of dict i...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0469/">
<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="For Python 3, PEP 3106 changed the design of the dict builtin and the mapping API in general to replace the separate list based and iterator based APIs in Python 2 with a merged, memory efficient set and multiset view based API. This new style of dict i...">
<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 469</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 469 Migration of dict iteration code to Python 3</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Alyssa Coghlan &lt;ncoghlan&#32;&#97;t&#32;gmail.com&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Removed from consideration by sponsor or authors">Withdrawn</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">18-Apr-2014</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.5</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even">18-Apr-2014, 21-Apr-2014</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="#pep-withdrawal">PEP Withdrawal</a></li>
<li><a class="reference internal" href="#mapping-iteration-models">Mapping iteration models</a><ul>
<li><a class="reference internal" href="#lists-as-mutable-snapshots">Lists as mutable snapshots</a></li>
<li><a class="reference internal" href="#iterator-objects">Iterator objects</a></li>
<li><a class="reference internal" href="#set-based-dynamic-views">Set based dynamic views</a></li>
</ul>
</li>
<li><a class="reference internal" href="#migrating-directly-to-python-3">Migrating directly to Python 3</a></li>
<li><a class="reference internal" href="#migrating-to-the-common-subset-of-python-2-and-3">Migrating to the common subset of Python 2 and 3</a></li>
<li><a class="reference internal" href="#migrating-from-python-3-to-the-common-subset-with-python-2-7">Migrating from Python 3 to the common subset with Python 2.7</a></li>
<li><a class="reference internal" href="#possible-changes-to-python-3-5">Possible changes to Python 3.5+</a></li>
<li><a class="reference internal" href="#discussion">Discussion</a></li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</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>For Python 3, <a class="pep reference internal" href="../pep-3106/" title="PEP 3106 Revamping dict.keys(), .values() and .items()">PEP 3106</a> changed the design of the <code class="docutils literal notranslate"><span class="pre">dict</span></code> builtin and the
mapping API in general to replace the separate list based and iterator based
APIs in Python 2 with a merged, memory efficient set and multiset view
based API. This new style of dict iteration was also added to the Python 2.7
<code class="docutils literal notranslate"><span class="pre">dict</span></code> type as a new set of iteration methods.</p>
<p>This means that there are now 3 different kinds of dict iteration that may
need to be migrated to Python 3 when an application makes the transition:</p>
<ul class="simple">
<li>Lists as mutable snapshots: <code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.items())</span></code></li>
<li>Iterator objects: <code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d.items())</span></code></li>
<li>Set based dynamic views: <code class="docutils literal notranslate"><span class="pre">d.viewitems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">d.items()</span></code></li>
</ul>
<p>There is currently no widely agreed best practice on how to reliably convert
all Python 2 dict iteration code to the common subset of Python 2 and 3,
especially when test coverage of the ported code is limited. This PEP
reviews the various ways the Python 2 iteration APIs may be accessed, and
looks at the available options for migrating that code to Python 3 by way of
the common subset of Python 2.6+ and Python 3.0+.</p>
<p>The PEP also considers the question of whether or not there are any
additions that may be worth making to Python 3.5 that may ease the
transition process for application code that doesnt need to worry about
supporting earlier versions when eventually making the leap to Python 3.</p>
</section>
<section id="pep-withdrawal">
<h2><a class="toc-backref" href="#pep-withdrawal" role="doc-backlink">PEP Withdrawal</a></h2>
<p>In writing the second draft of this PEP, I came to the conclusion that
the readability of hybrid Python 2/3 mapping code can actually be best
enhanced by better helper functions rather than by making changes to
Python 3.5+. The main value I now see in this PEP is as a clear record
of the recommended approaches to migrating mapping iteration code from
Python 2 to Python 3, as well as suggesting ways to keep things readable
and maintainable when writing hybrid code that supports both versions.</p>
<p>Notably, I recommend that hybrid code avoid calling mapping iteration
methods directly, and instead rely on builtin functions where possible,
and some additional helper functions for cases that would be a simple
combination of a builtin and a mapping method in pure Python 3 code, but
need to be handled slightly differently to get the exact same semantics in
Python 2.</p>
<p>Static code checkers like pylint could potentially be extended with an
optional warning regarding direct use of the mapping iteration methods in
a hybrid code base.</p>
</section>
<section id="mapping-iteration-models">
<h2><a class="toc-backref" href="#mapping-iteration-models" role="doc-backlink">Mapping iteration models</a></h2>
<p>Python 2.7 provides three different sets of methods to extract the keys,
values and items from a <code class="docutils literal notranslate"><span class="pre">dict</span></code> instance, accounting for 9 out of the
18 public methods of the <code class="docutils literal notranslate"><span class="pre">dict</span></code> type.</p>
<p>In Python 3, this has been rationalised to just 3 out of 11 public methods
(as the <code class="docutils literal notranslate"><span class="pre">has_key</span></code> method has also been removed).</p>
<section id="lists-as-mutable-snapshots">
<h3><a class="toc-backref" href="#lists-as-mutable-snapshots" role="doc-backlink">Lists as mutable snapshots</a></h3>
<p>This is the oldest of the three styles of dict iteration, and hence the
one implemented by the <code class="docutils literal notranslate"><span class="pre">d.keys()</span></code>, <code class="docutils literal notranslate"><span class="pre">d.values()</span></code> and <code class="docutils literal notranslate"><span class="pre">d.items()</span></code>
methods in Python 2.</p>
<p>These methods all return lists that are snapshots of the state of the
mapping at the time the method was called. This has a few consequences:</p>
<ul class="simple">
<li>the original object can be mutated freely without affecting iteration
over the snapshot</li>
<li>the snapshot can be modified independently of the original object</li>
<li>the snapshot consumes memory proportional to the size of the original
mapping</li>
</ul>
<p>The semantic equivalent of these operations in Python 3 are
<code class="docutils literal notranslate"><span class="pre">list(d.keys())</span></code>, <code class="docutils literal notranslate"><span class="pre">list(d.values())</span></code> and <code class="docutils literal notranslate"><span class="pre">list(d.iteritems())</span></code>.</p>
</section>
<section id="iterator-objects">
<h3><a class="toc-backref" href="#iterator-objects" role="doc-backlink">Iterator objects</a></h3>
<p>In Python 2.2, <code class="docutils literal notranslate"><span class="pre">dict</span></code> objects gained support for the then-new iterator
protocol, allowing direct iteration over the keys stored in the dictionary,
thus avoiding the need to build a list just to iterate over the dictionary
contents one entry at a time. <code class="docutils literal notranslate"><span class="pre">iter(d)</span></code> provides direct access to the
iterator object for the keys.</p>
<p>Python 2 also provides a <code class="docutils literal notranslate"><span class="pre">d.iterkeys()</span></code> method that is essentially
synonymous with <code class="docutils literal notranslate"><span class="pre">iter(d)</span></code>, along with <code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code> and
<code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> methods.</p>
<p>These iterators provide live views of the underlying object, and hence may
fail if the set of keys in the underlying object is changed during
iteration:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">d</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">d</span><span class="p">:</span>
<span class="gp">... </span> <span class="k">del</span> <span class="n">d</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
<span class="gp">...</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n">&lt;module&gt;</span>
<span class="gr">RuntimeError</span>: <span class="n">dictionary changed size during iteration</span>
</pre></div>
</div>
<p>As iterators, iteration over these objects is also a one-time operation:
once the iterator is exhausted, you have to go back to the original mapping
in order to iterate again.</p>
<p>In Python 3, direct iteration over mappings works the same way as it does
in Python 2. There are no method based equivalents - the semantic equivalents
of <code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code> and <code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> in Python 3 are
<code class="docutils literal notranslate"><span class="pre">iter(d.values())</span></code> and <code class="docutils literal notranslate"><span class="pre">iter(d.items())</span></code>.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">six</span></code> and <code class="docutils literal notranslate"><span class="pre">future.utils</span></code> compatibility modules also both provide
<code class="docutils literal notranslate"><span class="pre">iterkeys()</span></code>, <code class="docutils literal notranslate"><span class="pre">itervalues()</span></code> and <code class="docutils literal notranslate"><span class="pre">iteritems()</span></code> helper functions that
provide efficient iterator semantics in both Python 2 and 3.</p>
</section>
<section id="set-based-dynamic-views">
<h3><a class="toc-backref" href="#set-based-dynamic-views" role="doc-backlink">Set based dynamic views</a></h3>
<p>The model that is provided in Python 3 as a method based API is that of set
based dynamic views (technically multisets in the case of the <code class="docutils literal notranslate"><span class="pre">values()</span></code>
view).</p>
<p>In Python 3, the objects returned by <code class="docutils literal notranslate"><span class="pre">d.keys()</span></code>, <code class="docutils literal notranslate"><span class="pre">d.values()</span></code> and
<code class="docutils literal notranslate"><span class="pre">d.</span> <span class="pre">items()</span></code> provide a live view of the current state of
the underlying object, rather than taking a full snapshot of the current
state as they did in Python 2. This change is safe in many circumstances,
but does mean that, as with the direct iteration API, it is necessary to
avoid adding or removing keys during iteration, in order to avoid
encountering the following error:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">d</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="gp">... </span> <span class="k">del</span> <span class="n">d</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
<span class="gp">...</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n">&lt;module&gt;</span>
<span class="gr">RuntimeError</span>: <span class="n">dictionary changed size during iteration</span>
</pre></div>
</div>
<p>Unlike the iteration API, these objects are iterables, rather than iterators:
you can iterate over them multiple times, and each time they will iterate
over the entire underlying mapping.</p>
<p>These semantics are also available in Python 2.7 as the <code class="docutils literal notranslate"><span class="pre">d.viewkeys()</span></code>,
<code class="docutils literal notranslate"><span class="pre">d.viewvalues()</span></code> and <code class="docutils literal notranslate"><span class="pre">d.viewitems()</span></code> methods.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">future.utils</span></code> compatibility module also provides
<code class="docutils literal notranslate"><span class="pre">viewkeys()</span></code>, <code class="docutils literal notranslate"><span class="pre">viewvalues()</span></code> and <code class="docutils literal notranslate"><span class="pre">viewitems()</span></code> helper functions
when running on Python 2.7 or Python 3.x.</p>
</section>
</section>
<section id="migrating-directly-to-python-3">
<h2><a class="toc-backref" href="#migrating-directly-to-python-3" role="doc-backlink">Migrating directly to Python 3</a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">2to3</span></code> migration tool handles direct migrations to Python 3 in
accordance with the semantic equivalents described above:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">d.keys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.keys())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.values()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.values())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.items())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iterkeys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d.keys())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d.values())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d.items())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.viewkeys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">d.keys()</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.viewvalues()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">d.values()</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.viewitems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">d.items()</span></code></li>
</ul>
<p>Rather than 9 distinct mapping methods for iteration, there are now only the
3 view methods, which combine in straightforward ways with the two relevant
builtin functions to cover all of the behaviours that are available as
<code class="docutils literal notranslate"><span class="pre">dict</span></code> methods in Python 2.7.</p>
<p>Note that in many cases <code class="docutils literal notranslate"><span class="pre">d.keys()</span></code> can be replaced by just <code class="docutils literal notranslate"><span class="pre">d</span></code>, but the
<code class="docutils literal notranslate"><span class="pre">2to3</span></code> migration tool doesnt attempt that replacement.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">2to3</span></code> migration tool also <em>does not</em> provide any automatic assistance
for migrating references to these objects as bound or unbound methods - it
only automates conversions where the API is called immediately.</p>
</section>
<section id="migrating-to-the-common-subset-of-python-2-and-3">
<h2><a class="toc-backref" href="#migrating-to-the-common-subset-of-python-2-and-3" role="doc-backlink">Migrating to the common subset of Python 2 and 3</a></h2>
<p>When migrating to the common subset of Python 2 and 3, the above
transformations are not generally appropriate, as they all either result in
the creation of a redundant list in Python 2, have unexpectedly different
semantics in at least some cases, or both.</p>
<p>Since most code running in the common subset of Python 2 and 3 supports
at least as far back as Python 2.6, the currently recommended approach to
conversion of mapping iteration operation depends on two helper functions
for efficient iteration over mapping values and mapping item tuples:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">d.keys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.values()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(itervalues(d))</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(iteritems(d))</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iterkeys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">itervalues(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iteritems(d)</span></code></li>
</ul>
<p>Both <code class="docutils literal notranslate"><span class="pre">six</span></code> and <code class="docutils literal notranslate"><span class="pre">future.utils</span></code> provide appropriate definitions of
<code class="docutils literal notranslate"><span class="pre">itervalues()</span></code> and <code class="docutils literal notranslate"><span class="pre">iteritems()</span></code> (along with essentially redundant
definitions of <code class="docutils literal notranslate"><span class="pre">iterkeys()</span></code>). Creating your own definitions of these
functions in a custom compatibility module is also relatively
straightforward:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="nb">dict</span><span class="o">.</span><span class="n">iteritems</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="c1"># Python 3</span>
<span class="k">def</span> <span class="nf">itervalues</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">iter</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">iteritems</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">iter</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># Python 2</span>
<span class="k">def</span> <span class="nf">itervalues</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="n">d</span><span class="o">.</span><span class="n">itervalues</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">iteritems</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="n">d</span><span class="o">.</span><span class="n">iteritems</span><span class="p">()</span>
</pre></div>
</div>
<p>The greatest loss of readability currently arises when converting code that
actually <em>needs</em> the list based snapshots that were the default in Python
2. This readability loss could likely be mitigated by also providing
<code class="docutils literal notranslate"><span class="pre">listvalues</span></code> and <code class="docutils literal notranslate"><span class="pre">listitems</span></code> helper functions, allowing the affected
conversions to be simplified to:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">d.values()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">listvalues(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">listitems(d)</span></code></li>
</ul>
<p>The corresponding compatibility function definitions are as straightforward
as their iterator counterparts:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="nb">dict</span><span class="o">.</span><span class="n">iteritems</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="c1"># Python 3</span>
<span class="k">def</span> <span class="nf">listvalues</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">listitems</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># Python 2</span>
<span class="k">def</span> <span class="nf">listvalues</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="n">d</span><span class="o">.</span><span class="n">values</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">listitems</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="k">return</span> <span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
</pre></div>
</div>
<p>With that expanded set of compatibility functions, Python 2 code would
then be converted to “idiomatic” hybrid 2/3 code as:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">d.keys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.values()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">listvalues(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">listitems(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iterkeys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">itervalues(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iteritems(d)</span></code></li>
</ul>
<p>This compares well for readability with the idiomatic pure Python 3
code that uses the mapping methods and builtins directly:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">d.keys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.values()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.values())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.items())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iterkeys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d.values())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d.items())</span></code></li>
</ul>
<p>Its also notable that when using this approach, hybrid code would <em>never</em>
invoke the mapping methods directly: it would always invoke either a
builtin or helper function instead, in order to ensure the exact same
semantics on both Python 2 and 3.</p>
</section>
<section id="migrating-from-python-3-to-the-common-subset-with-python-2-7">
<h2><a class="toc-backref" href="#migrating-from-python-3-to-the-common-subset-with-python-2-7" role="doc-backlink">Migrating from Python 3 to the common subset with Python 2.7</a></h2>
<p>While the majority of migrations are currently from Python 2 either directly
to Python 3 or to the common subset of Python 2 and Python 3, there are also
some migrations of newer projects that start in Python 3 and then later
add Python 2 support, either due to user demand, or to gain access to
Python 2 libraries that are not yet available in Python 3 (and porting them
to Python 3 or creating a Python 3 compatible replacement is not a trivial
exercise).</p>
<p>In these cases, Python 2.7 compatibility is often sufficient, and the 2.7+
only view based helper functions provided by <code class="docutils literal notranslate"><span class="pre">future.utils</span></code> allow the bare
accesses to the Python 3 mapping view methods to be replaced with code that
is compatible with both Python 2.7 and Python 3 (note, this is the only
migration chart in the PEP that has Python 3 code on the left of the
conversion):</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">d.keys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">viewkeys(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.values()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">viewvalues(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">viewitems(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">list(d.keys())</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">list(d.values())</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">listvalues(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">list(d.items())</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">listitems(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">iter(d.keys())</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iter(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">iter(d.values())</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">itervalues(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">iter(d.items())</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">iteritems(d)</span></code></li>
</ul>
<p>As with migrations from Python 2 to the common subset, note that the hybrid
code ends up never invoking the mapping methods directly - it only calls
builtins and helper methods, with the latter addressing the semantic
differences between Python 2 and Python 3.</p>
</section>
<section id="possible-changes-to-python-3-5">
<h2><a class="toc-backref" href="#possible-changes-to-python-3-5" role="doc-backlink">Possible changes to Python 3.5+</a></h2>
<p>The main proposal put forward to potentially aid migration of existing
Python 2 code to Python 3 is the restoration of some or all of the
alternate iteration APIs to the Python 3 mapping API. In particular,
the initial draft of this PEP proposed making the following conversions
possible when migrating to the common subset of Python 2 and Python 3.5+:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">d.keys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.values()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.itervalues())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.items()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">list(d.iteritems())</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iterkeys()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">d.iterkeys()</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">d.itervalues()</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code> -&gt; <code class="docutils literal notranslate"><span class="pre">d.iteritems()</span></code></li>
</ul>
<p>Possible mitigations of the additional language complexity in Python 3
created by restoring these methods included immediately deprecating them,
as well as potentially hiding them from the <code class="docutils literal notranslate"><span class="pre">dir()</span></code> function (or perhaps
even defining a way to make <code class="docutils literal notranslate"><span class="pre">pydoc</span></code> aware of function deprecations).</p>
<p>However, in the case where the list output is actually desired, the end
result of that proposal is actually less readable than an appropriately
defined helper function, and the function and method forms of the iterator
versions are pretty much equivalent from a readability perspective.</p>
<p>So unless Ive missed something critical, readily available <code class="docutils literal notranslate"><span class="pre">listvalues()</span></code>
and <code class="docutils literal notranslate"><span class="pre">listitems()</span></code> helper functions look like they will improve the
readability of hybrid code more than anything we could add back to the
Python 3.5+ mapping API, and wont have any long-term impact on the
complexity of Python 3 itself.</p>
</section>
<section id="discussion">
<h2><a class="toc-backref" href="#discussion" role="doc-backlink">Discussion</a></h2>
<p>The fact that 5 years in to the Python 3 migration we still have users
considering the dict API changes a significant barrier to migration suggests
that there are problems with previously recommended approaches. This PEP
attempts to explore those issues and tries to isolate those cases where
previous advice (such as it was) could prove problematic.</p>
<p>My assessment (largely based on feedback from Twisted devs) is that
problems are most likely to arise when attempting to use <code class="docutils literal notranslate"><span class="pre">d.keys()</span></code>,
<code class="docutils literal notranslate"><span class="pre">d.values()</span></code>, and <code class="docutils literal notranslate"><span class="pre">d.items()</span></code> in hybrid code. While superficially it
seems as though there should be cases where it is safe to ignore the
semantic differences, in practice, the change from “mutable snapshot” to
“dynamic view” is significant enough that it is likely better
to just force the use of either list or iterator semantics for hybrid code,
and leave the use of the view semantics to pure Python 3 code.</p>
<p>This approach also creates rules that are simple enough and safe enough that
it should be possible to automate them in code modernisation scripts that
target the common subset of Python 2 and Python 3, just as <code class="docutils literal notranslate"><span class="pre">2to3</span></code> converts
them automatically when targeting pure Python 3 code.</p>
</section>
<section id="acknowledgements">
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
<p>Thanks to the folks at the Twisted sprint table at PyCon for a very
vigorous discussion of this idea (and several other topics), and especially
to Hynek Schlawack for acting as a moderator when things got a little too
heated :)</p>
<p>Thanks also to JP Calderone and Itamar Turner-Trauring for their email
feedback, as well to the participants in the <a class="reference external" href="https://mail.python.org/pipermail/python-dev/2014-April/134168.html">python-dev review</a> of
the initial version of the PEP.</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-0469.rst">https://github.com/python/peps/blob/main/peps/pep-0469.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0469.rst">2023-10-11 12:05:51 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#pep-withdrawal">PEP Withdrawal</a></li>
<li><a class="reference internal" href="#mapping-iteration-models">Mapping iteration models</a><ul>
<li><a class="reference internal" href="#lists-as-mutable-snapshots">Lists as mutable snapshots</a></li>
<li><a class="reference internal" href="#iterator-objects">Iterator objects</a></li>
<li><a class="reference internal" href="#set-based-dynamic-views">Set based dynamic views</a></li>
</ul>
</li>
<li><a class="reference internal" href="#migrating-directly-to-python-3">Migrating directly to Python 3</a></li>
<li><a class="reference internal" href="#migrating-to-the-common-subset-of-python-2-and-3">Migrating to the common subset of Python 2 and 3</a></li>
<li><a class="reference internal" href="#migrating-from-python-3-to-the-common-subset-with-python-2-7">Migrating from Python 3 to the common subset with Python 2.7</a></li>
<li><a class="reference internal" href="#possible-changes-to-python-3-5">Possible changes to Python 3.5+</a></li>
<li><a class="reference internal" href="#discussion">Discussion</a></li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</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-0469.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>