python-peps/pep-0468/index.html

555 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 468 Preserving the order of **kwargs in a function. | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0468/">
<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 468 Preserving the order of **kwargs in a function. | peps.python.org'>
<meta property="og:description" content="The **kwargs syntax in a function definition indicates that the interpreter should collect all keyword arguments that do not correspond to other named parameters. However, Python does not preserved the order in which those collected keyword arguments w...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0468/">
<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="The **kwargs syntax in a function definition indicates that the interpreter should collect all keyword arguments that do not correspond to other named parameters. However, Python does not preserved the order in which those collected keyword arguments w...">
<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 468</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 468 Preserving the order of **kwargs in a function.</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Eric Snow &lt;ericsnowcurrently&#32;&#97;t&#32;gmail.com&gt;</dd>
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/python-ideas&#64;python.org/">Python-Ideas list</a></dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-even">Type<span class="colon">:</span></dt>
<dd class="field-even"><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-odd">Created<span class="colon">:</span></dt>
<dd class="field-odd">05-Apr-2014</dd>
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
<dd class="field-even">3.6</dd>
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
<dd class="field-odd">05-Apr-2014, 08-Sep-2016</dd>
<dt class="field-even">Resolution<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2016-September/146329.html">Python-Dev message</a></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="#id6">Use Cases</a><ul>
<li><a class="reference internal" href="#serialization">Serialization</a></li>
<li><a class="reference internal" href="#debugging">Debugging</a></li>
<li><a class="reference internal" href="#other-use-cases">Other Use Cases</a></li>
</ul>
</li>
<li><a class="reference internal" href="#concerns">Concerns</a><ul>
<li><a class="reference internal" href="#performance">Performance</a></li>
<li><a class="reference internal" href="#other-python-implementations">Other Python Implementations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#relationship-to-unpacking-syntax">Relationship to **-unpacking syntax</a></li>
<li><a class="reference internal" href="#relationship-to-inspect-signature">Relationship to inspect.Signature</a></li>
<li><a class="reference internal" href="#c-api">C-API</a></li>
<li><a class="reference internal" href="#syntax">Syntax</a></li>
<li><a class="reference internal" href="#backward-compatibility">Backward-Compatibility</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#alternate-approaches">Alternate Approaches</a><ul>
<li><a class="reference internal" href="#opt-out-decorator">Opt-out Decorator</a></li>
<li><a class="reference internal" href="#opt-in-decorator">Opt-in Decorator</a></li>
<li><a class="reference internal" href="#kworder">__kworder__</a></li>
<li><a class="reference internal" href="#compact-dict-with-faster-iteration">Compact dict with faster iteration</a></li>
<li><a class="reference internal" href="#kwargs">***kwargs</a></li>
<li><a class="reference internal" href="#annotations">annotations</a></li>
<li><a class="reference internal" href="#dict-order">dict.__order__</a></li>
<li><a class="reference internal" href="#kwargsdict-order">KWArgsDict.__order__</a></li>
</ul>
</li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
<li><a class="reference internal" href="#footnotes">Footnotes</a></li>
<li><a class="reference internal" href="#references">References</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>The **kwargs syntax in a function definition indicates that the
interpreter should collect all keyword arguments that do not correspond
to other named parameters. However, Python does not preserved the
order in which those collected keyword arguments were passed to the
function. In some contexts the order matters. This PEP dictates that
the collected keyword arguments be exposed in the function body as an
ordered mapping.</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>Pythons **kwargs syntax in function definitions provides a powerful
means of dynamically handling keyword arguments. In some applications
of the syntax (see <span class="target" id="use-cases">Use Cases</span>), the semantics applied to the
collected keyword arguments requires that order be preserved.
Unsurprisingly, this is similar to how OrderedDict is related to dict.</p>
<p>Currently to preserved the order you have to do so manually and
separately from the actual function call. This involves building an
ordered mapping, whether an OrderedDict or an iterable of 2-tuples,
which is then passed as a single argument to the function.
<a class="footnote-reference brackets" href="#arg-unpacking" id="id1">[1]</a></p>
<p>With the capability described in this PEP, that boilerplate would no
longer be required.</p>
<p>For comparison, currently:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">kwargs</span> <span class="o">=</span> <span class="n">OrderedDict</span><span class="p">()</span>
<span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;eggs&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="o">...</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">spam</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</div>
<p>and with this proposal:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">spam</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Alyssa (Nick) Coghlan, speaking of some of the uses cases, summed it up well
<a class="footnote-reference brackets" href="#alyssa-obvious" id="id2">[2]</a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">These</span> <span class="o">*</span><span class="n">can</span><span class="o">*</span> <span class="nb">all</span> <span class="n">be</span> <span class="n">done</span> <span class="n">today</span><span class="p">,</span> <span class="n">but</span> <span class="o">*</span><span class="ow">not</span><span class="o">*</span> <span class="n">by</span> <span class="n">using</span> <span class="n">keyword</span> <span class="n">arguments</span><span class="o">.</span>
<span class="n">In</span> <span class="n">my</span> <span class="n">view</span><span class="p">,</span> <span class="n">the</span> <span class="n">problem</span> <span class="n">to</span> <span class="n">be</span> <span class="n">addressed</span> <span class="ow">is</span> <span class="n">that</span> <span class="n">keyword</span> <span class="n">arguments</span>
<span class="o">*</span><span class="n">look</span><span class="o">*</span> <span class="n">like</span> <span class="n">they</span> <span class="n">should</span> <span class="n">work</span> <span class="k">for</span> <span class="n">these</span> <span class="n">cases</span><span class="p">,</span> <span class="n">because</span> <span class="n">they</span> <span class="n">have</span> <span class="n">a</span>
<span class="n">definite</span> <span class="n">order</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">source</span> <span class="n">code</span><span class="o">.</span> <span class="n">The</span> <span class="n">only</span> <span class="n">reason</span> <span class="n">they</span> <span class="n">don</span><span class="s1">&#39;t work</span>
<span class="ow">is</span> <span class="n">because</span> <span class="n">the</span> <span class="n">interpreter</span> <span class="n">throws</span> <span class="n">that</span> <span class="n">ordering</span> <span class="n">information</span> <span class="n">away</span><span class="o">.</span>
<span class="n">It</span><span class="s1">&#39;s a textbook case of a language feature becoming an attractive</span>
<span class="n">nuisance</span> <span class="ow">in</span> <span class="n">some</span> <span class="n">circumstances</span><span class="p">:</span> <span class="n">the</span> <span class="n">simple</span> <span class="ow">and</span> <span class="n">obvious</span> <span class="n">solution</span> <span class="k">for</span>
<span class="n">the</span> <span class="n">above</span> <span class="n">use</span> <span class="n">cases</span> <span class="o">*</span><span class="n">doesn</span><span class="s1">&#39;t actually work* for reasons that aren&#39;</span><span class="n">t</span>
<span class="n">obviously</span> <span class="n">clear</span> <span class="k">if</span> <span class="n">you</span> <span class="n">don</span><span class="s1">&#39;t have a firm grasp of Python&#39;</span><span class="n">s</span> <span class="n">admittedly</span>
<span class="n">complicated</span> <span class="n">argument</span> <span class="n">handling</span><span class="o">.</span>
</pre></div>
</div>
<p>This observation is supported by the appearance of this proposal over
the years and the numerous times that people have been confused by the
constructor for OrderedDict. <a class="footnote-reference brackets" href="#past-threads" id="id3">[3]</a> <a class="footnote-reference brackets" href="#loss-of-order" id="id4">[4]</a>
<a class="footnote-reference brackets" href="#compact-dict" id="id5">[5]</a></p>
</section>
<section id="id6">
<h2><a class="toc-backref" href="#id6" role="doc-backlink">Use Cases</a></h2>
<p>As Alyssa noted, the current behavior of **kwargs is unintuitive in
cases where one would expect order to matter. Aside from more specific
cases outlined below, in general “anything else where you want to
control the iteration order <em>and</em> set field names and values in a single
call will potentially benefit.” <a class="footnote-reference brackets" href="#alyssa-general" id="id7">[6]</a> That matters in the
case of factories (e.g. __init__()) for ordered types.</p>
<section id="serialization">
<h3><a class="toc-backref" href="#serialization" role="doc-backlink">Serialization</a></h3>
<p>Obviously OrderedDict would benefit (both __init__() and update()) from
ordered kwargs. However, the benefit also extends to serialization
APIs <a class="footnote-reference brackets" href="#alyssa-obvious" id="id8">[2]</a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">In</span> <span class="n">the</span> <span class="n">context</span> <span class="n">of</span> <span class="n">serialisation</span><span class="p">,</span> <span class="n">one</span> <span class="n">key</span> <span class="n">lesson</span> <span class="n">we</span> <span class="n">have</span> <span class="n">learned</span> <span class="ow">is</span>
<span class="n">that</span> <span class="n">arbitrary</span> <span class="n">ordering</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">problem</span> <span class="n">when</span> <span class="n">you</span> <span class="n">want</span> <span class="n">to</span> <span class="n">minimise</span>
<span class="n">spurious</span> <span class="n">diffs</span><span class="p">,</span> <span class="ow">and</span> <span class="n">sorting</span> <span class="n">isn</span><span class="s1">&#39;t a simple solution.</span>
<span class="n">Tools</span> <span class="n">like</span> <span class="n">doctest</span> <span class="n">don</span><span class="s1">&#39;t tolerate spurious diffs at all, but are</span>
<span class="n">often</span> <span class="n">amenable</span> <span class="n">to</span> <span class="n">a</span> <span class="n">sorting</span> <span class="n">based</span> <span class="n">answer</span><span class="o">.</span>
<span class="n">The</span> <span class="n">cases</span> <span class="n">where</span> <span class="n">it</span> <span class="n">would</span> <span class="n">be</span> <span class="n">highly</span> <span class="n">desirable</span> <span class="n">to</span> <span class="n">be</span> <span class="n">able</span> <span class="n">use</span> <span class="n">keyword</span>
<span class="n">arguments</span> <span class="n">to</span> <span class="n">control</span> <span class="n">the</span> <span class="n">order</span> <span class="n">of</span> <span class="n">display</span> <span class="n">of</span> <span class="n">a</span> <span class="n">collection</span> <span class="n">of</span> <span class="n">key</span>
<span class="n">value</span> <span class="n">pairs</span> <span class="n">are</span> <span class="n">ones</span> <span class="n">like</span><span class="p">:</span>
<span class="o">*</span> <span class="n">printing</span> <span class="n">out</span> <span class="n">key</span><span class="p">:</span><span class="n">value</span> <span class="n">pairs</span> <span class="ow">in</span> <span class="n">CLI</span> <span class="n">output</span>
<span class="o">*</span> <span class="n">mapping</span> <span class="n">semantic</span> <span class="n">names</span> <span class="n">to</span> <span class="n">column</span> <span class="n">order</span> <span class="ow">in</span> <span class="n">a</span> <span class="n">CSV</span>
<span class="o">*</span> <span class="n">serialising</span> <span class="n">attributes</span> <span class="ow">and</span> <span class="n">elements</span> <span class="ow">in</span> <span class="n">particular</span> <span class="n">orders</span> <span class="ow">in</span> <span class="n">XML</span>
<span class="o">*</span> <span class="n">serialising</span> <span class="nb">map</span> <span class="n">keys</span> <span class="ow">in</span> <span class="n">particular</span> <span class="n">orders</span> <span class="ow">in</span> <span class="n">human</span> <span class="n">readable</span> <span class="n">formats</span>
<span class="n">like</span> <span class="n">JSON</span> <span class="ow">and</span> <span class="n">YAML</span> <span class="p">(</span><span class="n">particularly</span> <span class="n">when</span> <span class="n">they</span><span class="s1">&#39;re going to be placed</span>
<span class="n">under</span> <span class="n">source</span> <span class="n">control</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="debugging">
<h3><a class="toc-backref" href="#debugging" role="doc-backlink">Debugging</a></h3>
<p>In the words of Raymond Hettinger <a class="footnote-reference brackets" href="#raymond-debug" id="id9">[7]</a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">It</span> <span class="n">makes</span> <span class="n">it</span> <span class="n">easier</span> <span class="n">to</span> <span class="n">debug</span> <span class="k">if</span> <span class="n">the</span> <span class="n">arguments</span> <span class="n">show</span><span class="o">-</span><span class="n">up</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">order</span>
<span class="n">they</span> <span class="n">were</span> <span class="n">created</span><span class="o">.</span> <span class="n">AFAICT</span><span class="p">,</span> <span class="n">no</span> <span class="n">purpose</span> <span class="ow">is</span> <span class="n">served</span> <span class="n">by</span> <span class="n">scrambling</span> <span class="n">them</span><span class="o">.</span>
</pre></div>
</div>
</section>
<section id="other-use-cases">
<h3><a class="toc-backref" href="#other-use-cases" role="doc-backlink">Other Use Cases</a></h3>
<ul class="simple">
<li>Mock objects. <a class="footnote-reference brackets" href="#mock" id="id10">[8]</a></li>
<li>Controlling object presentation.</li>
<li>Alternate namedtuple() where defaults can be specified.</li>
<li>Specifying argument priority by order.</li>
</ul>
</section>
</section>
<section id="concerns">
<h2><a class="toc-backref" href="#concerns" role="doc-backlink">Concerns</a></h2>
<section id="performance">
<h3><a class="toc-backref" href="#performance" role="doc-backlink">Performance</a></h3>
<p>As already noted, the idea of ordered keyword arguments has come up on
a number of occasions. Each time it has been met with the same
response, namely that preserving keyword arg order would have a
sufficiently adverse effect on function call performance that its not
worth doing. However, Guido noted the following <a class="footnote-reference brackets" href="#guido-open" id="id11">[9]</a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Making</span> <span class="o">**</span><span class="n">kwds</span> <span class="n">ordered</span> <span class="ow">is</span> <span class="n">still</span> <span class="nb">open</span><span class="p">,</span> <span class="n">but</span> <span class="n">requires</span> <span class="n">careful</span> <span class="n">design</span> <span class="ow">and</span>
<span class="n">implementation</span> <span class="n">to</span> <span class="n">avoid</span> <span class="n">slowing</span> <span class="n">down</span> <span class="n">function</span> <span class="n">calls</span> <span class="n">that</span> <span class="n">don</span><span class="s1">&#39;t benefit.</span>
</pre></div>
</div>
<p>As will be noted below, there are ways to work around this at the
expense of increased complication. Ultimately the simplest approach is
the one that makes the most sense: pack collected key word arguments
into an OrderedDict. However, without a C implementation of OrderedDict
there isnt much to discuss. That changed in Python 3.5.
<a class="footnote-reference brackets" href="#c-ordereddict" id="id12">[10]</a></p>
<p>Note: in Python 3.6 dict is order-preserving. This virtually eliminates
performance concerns.</p>
</section>
<section id="other-python-implementations">
<h3><a class="toc-backref" href="#other-python-implementations" role="doc-backlink">Other Python Implementations</a></h3>
<p>Another important issue to consider is that new features must be
cognizant of the multiple Python implementations. At some point each of
them would be expected to have implemented ordered kwargs. In this
regard there doesnt seem to be an issue with the idea. <a class="footnote-reference brackets" href="#ironpython" id="id13">[11]</a>
An informal survey of the major Python implementations has indicated
that this feature will not be a significant burden.</p>
</section>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>Starting in version 3.6 Python will preserve the order of keyword
arguments as passed to a function. To accomplish this the collected
kwargs will now be an ordered mapping. Note that this does not necessarily
mean OrderedDict. dict in CPython 3.6 is now ordered, similar to PyPy.</p>
<p>This will apply only to functions for which the definition uses the
**kwargs syntax for collecting otherwise unspecified keyword
arguments. Only the order of those keyword arguments will be
preserved.</p>
<section id="relationship-to-unpacking-syntax">
<h3><a class="toc-backref" href="#relationship-to-unpacking-syntax" role="doc-backlink">Relationship to **-unpacking syntax</a></h3>
<p>The ** unpacking syntax in function calls has no special connection with
this proposal. Keyword arguments provided by unpacking will be treated
in exactly the same way as they are now: ones that match defined
parameters are gather there and the remainder will be collected into the
ordered kwargs (just like any other unmatched keyword argument).</p>
<p>Note that unpacking a mapping with undefined order, such as dict, will
preserve its iteration order like normal. Its just that the order will
remain undefined. The ordered mapping into which the unpacked key-value
pairs will then be packed will not be able to provide any alternate
ordering. This should not be surprising.</p>
<p>There have been brief discussions of simply passing these mappings
through to the functions kwargs without unpacking and repacking them,
but that is both outside the scope of this proposal and probably a bad
idea regardless. (There is a reason those discussions were brief.)</p>
</section>
<section id="relationship-to-inspect-signature">
<h3><a class="toc-backref" href="#relationship-to-inspect-signature" role="doc-backlink">Relationship to inspect.Signature</a></h3>
<p>Signature objects should need no changes. The <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> parameter of
inspect.BoundArguments (returned by Signature.bind() and
Signature.bind_partial()) will change from a dict to an OrderedDict.</p>
</section>
<section id="c-api">
<h3><a class="toc-backref" href="#c-api" role="doc-backlink">C-API</a></h3>
<p>No changes.</p>
</section>
<section id="syntax">
<h3><a class="toc-backref" href="#syntax" role="doc-backlink">Syntax</a></h3>
<p>No syntax is added or changed by this proposal.</p>
</section>
<section id="backward-compatibility">
<h3><a class="toc-backref" href="#backward-compatibility" role="doc-backlink">Backward-Compatibility</a></h3>
<p>The following will change:</p>
<ul class="simple">
<li>iteration order of kwargs will now be consistent (except of course in
the case described above)</li>
</ul>
</section>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>For CPython theres nothing to do.</p>
</section>
<section id="alternate-approaches">
<h2><a class="toc-backref" href="#alternate-approaches" role="doc-backlink">Alternate Approaches</a></h2>
<section id="opt-out-decorator">
<h3><a class="toc-backref" href="#opt-out-decorator" role="doc-backlink">Opt-out Decorator</a></h3>
<p>This is identical to the current proposal with the exception that Python
would also provide a decorator in functools that would cause collected
keyword arguments to be packed into a normal dict instead of an
OrderedDict.</p>
<p>Prognosis:</p>
<p>This would only be necessary if performance is determined to be
significantly different in some uncommon cases or that there are other
backward-compatibility concerns that cannot be resolved otherwise.</p>
</section>
<section id="opt-in-decorator">
<h3><a class="toc-backref" href="#opt-in-decorator" role="doc-backlink">Opt-in Decorator</a></h3>
<p>The status quo would be unchanged. Instead Python would provide a
decorator in functools that would register or mark the decorated
function as one that should get ordered keyword arguments. The
performance overhead to check the function at call time would be
marginal.</p>
<p>Prognosis:</p>
<p>The only real down-side is in the case of function wrappers factories
(e.g. functools.partial and many decorators) that aim to perfectly
preserve keyword arguments by using kwargs in the wrapper definition
and kwargs unpacking in the call to the wrapped function. Each wrapper
would have to be updated separately, though having functools.wraps() do
this automaticallywould help.</p>
</section>
<section id="kworder">
<h3><a class="toc-backref" href="#kworder" role="doc-backlink">__kworder__</a></h3>
<p>The order of keyword arguments would be stored separately in a list at
call time. The list would be bound to __kworder__ in the function
locals.</p>
<p>Prognosis:</p>
<p>This likewise complicates the wrapper case.</p>
</section>
<section id="compact-dict-with-faster-iteration">
<h3><a class="toc-backref" href="#compact-dict-with-faster-iteration" role="doc-backlink">Compact dict with faster iteration</a></h3>
<p>Raymond Hettinger has introduced the idea of a dict implementation that
would result in preserving insertion order on dicts (until the first
deletion). This would be a perfect fit for kwargs. <a class="footnote-reference brackets" href="#compact-dict" id="id14">[5]</a></p>
<p>Prognosis:</p>
<p>The idea is still uncertain in both viability and timeframe.</p>
<p>Note that Python 3.6 now has this dict implementation.</p>
</section>
<section id="kwargs">
<h3><a class="toc-backref" href="#kwargs" role="doc-backlink">***kwargs</a></h3>
<p>This would add a new form to a functions signature as a mutually
exclusive parallel to **kwargs. The new syntax, ***kwargs (note
that there are three asterisks), would indicate that kwargs should
preserve the order of keyword arguments.</p>
<p>Prognosis:</p>
<p>New syntax is only added to Python under the most <em>dire</em> circumstances.
With other available solutions, new syntax is not justifiable.
Furthermore, like all opt-in solutions, the new syntax would complicate
the pass-through case.</p>
</section>
<section id="annotations">
<h3><a class="toc-backref" href="#annotations" role="doc-backlink">annotations</a></h3>
<p>This is a variation on the decorator approach. Instead of using a
decorator to mark the function, you would use a function annotation on
**kwargs.</p>
<p>Prognosis:</p>
<p>In addition to the pass-through complication, annotations have been
actively discouraged in Python core development. Use of annotations to
opt-in to order preservation runs the risk of interfering with other
application-level use of annotations.</p>
</section>
<section id="dict-order">
<h3><a class="toc-backref" href="#dict-order" role="doc-backlink">dict.__order__</a></h3>
<p>dict objects would have a new attribute, <code class="docutils literal notranslate"><span class="pre">__order__</span></code> that would default
to None and that in the kwargs case the interpreter would use in the
same way as described above for __kworder__.</p>
<p>Prognosis:</p>
<p>It would mean zero impact on kwargs performance but the change would be
pretty intrusive (Python uses dict a lot). Also, for the wrapper case
the interpreter would have to be careful to preserve <code class="docutils literal notranslate"><span class="pre">__order__</span></code>.</p>
</section>
<section id="kwargsdict-order">
<h3><a class="toc-backref" href="#kwargsdict-order" role="doc-backlink">KWArgsDict.__order__</a></h3>
<p>This is the same as the <code class="docutils literal notranslate"><span class="pre">dict.__order__</span></code> idea, but kwargs would be an
instance of a new minimal dict subclass that provides the <code class="docutils literal notranslate"><span class="pre">__order__</span></code>
attribute. dict would instead be unchanged.</p>
<p>Prognosis:</p>
<p>Simply switching to OrderedDict is a less complicated and more intuitive
change.</p>
</section>
</section>
<section id="acknowledgements">
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
<p>Thanks to Andrew Barnert for helpful feedback and to the participants of
all the past email threads.</p>
</section>
<section id="footnotes">
<h2><a class="toc-backref" href="#footnotes" role="doc-backlink">Footnotes</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="arg-unpacking" role="doc-footnote">
<dt class="label" id="arg-unpacking">[<a href="#id1">1</a>]</dt>
<dd>Alternately, you could also replace ** in your function definition
with * and then pass in key/value 2-tuples. This has the advantage
of not requiring the keys to be valid identifier strings. See
<a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-April/027491.html">https://mail.python.org/pipermail/python-ideas/2014-April/027491.html</a>.</aside>
</aside>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="alyssa-obvious" role="doc-footnote">
<dt class="label" id="alyssa-obvious">[2]<em> (<a href='#id2'>1</a>, <a href='#id8'>2</a>) </em></dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-April/027512.html">https://mail.python.org/pipermail/python-ideas/2014-April/027512.html</a></aside>
<aside class="footnote brackets" id="past-threads" role="doc-footnote">
<dt class="label" id="past-threads">[<a href="#id3">3</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2009-April/004163.html">https://mail.python.org/pipermail/python-ideas/2009-April/004163.html</a><p><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2010-October/008445.html">https://mail.python.org/pipermail/python-ideas/2010-October/008445.html</a></p>
<p><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2011-January/009037.html">https://mail.python.org/pipermail/python-ideas/2011-January/009037.html</a></p>
<p><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2013-February/019690.html">https://mail.python.org/pipermail/python-ideas/2013-February/019690.html</a></p>
<p><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2013-May/020727.html">https://mail.python.org/pipermail/python-ideas/2013-May/020727.html</a></p>
<p><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-March/027225.html">https://mail.python.org/pipermail/python-ideas/2014-March/027225.html</a></p>
<p><a class="reference external" href="http://bugs.python.org/issue16276">http://bugs.python.org/issue16276</a></p>
<p><a class="reference external" href="http://bugs.python.org/issue16553">http://bugs.python.org/issue16553</a></p>
<p><a class="reference external" href="http://bugs.python.org/issue19026">http://bugs.python.org/issue19026</a></p>
<p><a class="reference external" href="http://bugs.python.org/issue5397#msg82972">http://bugs.python.org/issue5397#msg82972</a></p>
</aside>
<aside class="footnote brackets" id="loss-of-order" role="doc-footnote">
<dt class="label" id="loss-of-order">[<a href="#id4">4</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2007-February/071310.html">https://mail.python.org/pipermail/python-dev/2007-February/071310.html</a></aside>
<aside class="footnote brackets" id="compact-dict" role="doc-footnote">
<dt class="label" id="compact-dict">[5]<em> (<a href='#id5'>1</a>, <a href='#id14'>2</a>) </em></dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2012-December/123028.html">https://mail.python.org/pipermail/python-dev/2012-December/123028.html</a><blockquote>
<div><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2012-December/123105.html">https://mail.python.org/pipermail/python-dev/2012-December/123105.html</a></div></blockquote>
<p><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2013-May/126327.html">https://mail.python.org/pipermail/python-dev/2013-May/126327.html</a></p>
<blockquote>
<div><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2013-May/126328.html">https://mail.python.org/pipermail/python-dev/2013-May/126328.html</a></div></blockquote>
</aside>
<aside class="footnote brackets" id="alyssa-general" role="doc-footnote">
<dt class="label" id="alyssa-general">[<a href="#id7">6</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2012-December/123105.html">https://mail.python.org/pipermail/python-dev/2012-December/123105.html</a></aside>
<aside class="footnote brackets" id="raymond-debug" role="doc-footnote">
<dt class="label" id="raymond-debug">[<a href="#id9">7</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2013-May/126327.html">https://mail.python.org/pipermail/python-dev/2013-May/126327.html</a></aside>
<aside class="footnote brackets" id="mock" role="doc-footnote">
<dt class="label" id="mock">[<a href="#id10">8</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2009-April/004163.html">https://mail.python.org/pipermail/python-ideas/2009-April/004163.html</a><blockquote>
<div><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2009-April/004165.html">https://mail.python.org/pipermail/python-ideas/2009-April/004165.html</a><p><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2009-April/004175.html">https://mail.python.org/pipermail/python-ideas/2009-April/004175.html</a></p>
</div></blockquote>
</aside>
<aside class="footnote brackets" id="guido-open" role="doc-footnote">
<dt class="label" id="guido-open">[<a href="#id11">9</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2013-May/126404.html">https://mail.python.org/pipermail/python-dev/2013-May/126404.html</a></aside>
<aside class="footnote brackets" id="c-ordereddict" role="doc-footnote">
<dt class="label" id="c-ordereddict">[<a href="#id12">10</a>]</dt>
<dd><a class="reference external" href="http://bugs.python.org/issue16991">http://bugs.python.org/issue16991</a></aside>
<aside class="footnote brackets" id="ironpython" role="doc-footnote">
<dt class="label" id="ironpython">[<a href="#id13">11</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2012-December/123100.html">https://mail.python.org/pipermail/python-dev/2012-December/123100.html</a></aside>
</aside>
</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-0468.rst">https://github.com/python/peps/blob/main/peps/pep-0468.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0468.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="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#id6">Use Cases</a><ul>
<li><a class="reference internal" href="#serialization">Serialization</a></li>
<li><a class="reference internal" href="#debugging">Debugging</a></li>
<li><a class="reference internal" href="#other-use-cases">Other Use Cases</a></li>
</ul>
</li>
<li><a class="reference internal" href="#concerns">Concerns</a><ul>
<li><a class="reference internal" href="#performance">Performance</a></li>
<li><a class="reference internal" href="#other-python-implementations">Other Python Implementations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#relationship-to-unpacking-syntax">Relationship to **-unpacking syntax</a></li>
<li><a class="reference internal" href="#relationship-to-inspect-signature">Relationship to inspect.Signature</a></li>
<li><a class="reference internal" href="#c-api">C-API</a></li>
<li><a class="reference internal" href="#syntax">Syntax</a></li>
<li><a class="reference internal" href="#backward-compatibility">Backward-Compatibility</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#alternate-approaches">Alternate Approaches</a><ul>
<li><a class="reference internal" href="#opt-out-decorator">Opt-out Decorator</a></li>
<li><a class="reference internal" href="#opt-in-decorator">Opt-in Decorator</a></li>
<li><a class="reference internal" href="#kworder">__kworder__</a></li>
<li><a class="reference internal" href="#compact-dict-with-faster-iteration">Compact dict with faster iteration</a></li>
<li><a class="reference internal" href="#kwargs">***kwargs</a></li>
<li><a class="reference internal" href="#annotations">annotations</a></li>
<li><a class="reference internal" href="#dict-order">dict.__order__</a></li>
<li><a class="reference internal" href="#kwargsdict-order">KWArgsDict.__order__</a></li>
</ul>
</li>
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
<li><a class="reference internal" href="#footnotes">Footnotes</a></li>
<li><a class="reference internal" href="#references">References</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-0468.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>