495 lines
42 KiB
HTML
495 lines
42 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 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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </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 <ncoghlan at gmail.com></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> -> <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> -> <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> -> <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 doesn’t 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">>>> </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">>>> </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">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></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">>>> </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">>>> </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">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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 doesn’t 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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <code class="docutils literal notranslate"><span class="pre">iter(d.items())</span></code></li>
|
||
</ul>
|
||
<p>It’s 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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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> -> <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 I’ve 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 won’t 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> |