peps/pep-0637/index.html

1215 lines
114 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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 637 Support for indexing with keyword arguments | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0637/">
<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 637 Support for indexing with keyword arguments | peps.python.org'>
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0637/">
<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="Python Enhancement Proposals (PEPs)">
<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 637</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 637 Support for indexing with keyword arguments</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Stefano Borini</dd>
<dt class="field-even">Sponsor<span class="colon">:</span></dt>
<dd class="field-even">Steven DAprano</dd>
<dt class="field-odd">Discussions-To<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/python-ideas&#64;python.org/">Python-Ideas list</a></dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-even">Created<span class="colon">:</span></dt>
<dd class="field-even">24-Aug-2020</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.10</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even">23-Sep-2020</dd>
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/6TAQ2BEVSJNV4JM2RJYSSYFJUT3INGZD/">Python-Dev thread</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="#overview">Overview</a><ul>
<li><a class="reference internal" href="#background">Background</a></li>
<li><a class="reference internal" href="#use-cases">Use cases</a></li>
<li><a class="reference internal" href="#current-status-of-indexing-operation">Current status of indexing operation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#indexing-behavior-in-standard-classes-dict-list-etc">Indexing behavior in standard classes (dict, list, etc.)</a></li>
<li><a class="reference internal" href="#corner-case-and-gotchas">Corner case and Gotchas</a></li>
</ul>
</li>
<li><a class="reference internal" href="#c-interface">C Interface</a></li>
<li><a class="reference internal" href="#how-to-teach-recommendations">“How to teach” recommendations</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#workarounds">Workarounds</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#previous-pep-472-solutions">Previous PEP 472 solutions</a></li>
<li><a class="reference internal" href="#adding-new-dunders">Adding new dunders</a></li>
<li><a class="reference internal" href="#adding-an-adapter-function">Adding an adapter function</a></li>
<li><a class="reference internal" href="#create-a-new-kwslice-object">create a new “kwslice” object</a></li>
<li><a class="reference internal" href="#using-a-single-bit-to-change-the-behavior">Using a single bit to change the behavior</a></li>
<li><a class="reference internal" href="#allowing-for-empty-index-notation-obj">Allowing for empty index notation obj[]</a></li>
<li><a class="reference internal" href="#sentinel-value-for-no-given-positional-index">Sentinel value for no given positional index</a></li>
</ul>
</li>
<li><a class="reference internal" href="#common-objections">Common objections</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>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This PEP has been rejected. In general, the cost of introducing new syntax
was not outweighed by the perceived benefits. See the link in the Resolution
header field for details.</p>
</div>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>At present keyword arguments are allowed in function calls, but not in
item access. This PEP proposes that Python be extended to allow keyword
arguments in item access.</p>
<p>The following example shows keyword arguments for ordinary function calls:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">val</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
<p>The proposal would extend the syntax to allow a similar construct
to indexing operations:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">val</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">]</span> <span class="c1"># getitem</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span> <span class="c1"># setitem</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">del</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">]</span> <span class="c1"># delitem</span>
</pre></div>
</div>
<p>and would also provide appropriate semantics. Single- and double-star unpacking of
arguments is also provided:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">val</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="o">**</span><span class="p">{</span><span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">}]</span> <span class="c1"># Equivalent to above.</span>
</pre></div>
</div>
<p>This PEP is a successor to <a class="pep reference internal" href="../pep-0472/" title="PEP 472 Support for indexing with keyword arguments">PEP 472</a>, which was rejected due to lack of
interest in 2019. Since then theres been renewed interest in the feature.</p>
</section>
<section id="overview">
<h2><a class="toc-backref" href="#overview" role="doc-backlink">Overview</a></h2>
<section id="background">
<h3><a class="toc-backref" href="#background" role="doc-backlink">Background</a></h3>
<p><a class="pep reference internal" href="../pep-0472/" title="PEP 472 Support for indexing with keyword arguments">PEP 472</a> was opened in 2014. The PEP detailed various use cases and was created by
extracting implementation strategies from a broad discussion on the
python-ideas mailing list, although no clear consensus was reached on which strategy
should be used. Many corner cases have been examined more closely and felt
awkward, backward incompatible or both.</p>
<p>The PEP was eventually rejected in 2019 <a class="footnote-reference brackets" href="#rejection" id="id1">[1]</a> mostly
due to lack of interest for the feature despite its 5 years of existence.</p>
<p>However, with the introduction of type hints in <a class="pep reference internal" href="../pep-0484/" title="PEP 484 Type Hints">PEP 484</a> the
square bracket notation has been used consistently to enrich the typing
annotations, e.g. to specify a list of integers as Sequence[int]. Additionally,
there has been an expanded growth of packages for data analysis such as pandas
and xarray, which use names to describe columns in a table (pandas) or axis in
an nd-array (xarray). These packages allow users to access specific data by
names, but cannot currently use index notation ([]) for this functionality.</p>
<p>As a result, a renewed interest in a more flexible syntax that would allow for
named information has been expressed occasionally in many different threads on
python-ideas, recently by Caleb Donovick <a class="footnote-reference brackets" href="#request-1" id="id2">[2]</a> in 2019 and Andras
Tantos <a class="footnote-reference brackets" href="#request-2" id="id3">[3]</a> in 2020. These requests prompted a strong activity on the
python-ideas mailing list, where the various options have been re-discussed and
a general consensus on an implementation strategy has now been reached.</p>
</section>
<section id="use-cases">
<h3><a class="toc-backref" href="#use-cases" role="doc-backlink">Use cases</a></h3>
<p>The following practical use cases present different cases where a keyword
specification would improve notation and provide additional value:</p>
<ol class="arabic">
<li>To provide a more communicative meaning to the index, preventing e.g. accidental
inversion of indexes:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">grid_position</span><span class="p">[</span><span class="n">x</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">z</span><span class="o">=</span><span class="mi">8</span><span class="p">]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">rain_amount</span><span class="p">[</span><span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">:</span><span class="mi">12</span><span class="p">,</span> <span class="n">location</span><span class="o">=</span><span class="n">location</span><span class="p">]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">matrix</span><span class="p">[</span><span class="n">row</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">col</span><span class="o">=</span><span class="mi">40</span><span class="p">]</span>
</pre></div>
</div>
</li>
<li>To enrich the typing notation with keywords, especially during the use of generics:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">function</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="n">MyType</span><span class="p">[</span><span class="n">T</span><span class="o">=</span><span class="nb">int</span><span class="p">]):</span>
</pre></div>
</div>
</li>
<li>In some domain, such as computational physics and chemistry, the use of a
notation such as <code class="docutils literal notranslate"><span class="pre">Basis[Z=5]</span></code> is a Domain Specific Language notation to represent
a level of accuracy:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">low_accuracy_energy</span> <span class="o">=</span> <span class="n">computeEnergy</span><span class="p">(</span><span class="n">molecule</span><span class="p">,</span> <span class="n">BasisSet</span><span class="p">[</span><span class="n">Z</span><span class="o">=</span><span class="mi">3</span><span class="p">])</span>
</pre></div>
</div>
</li>
<li>Pandas currently uses a notation such as:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">df</span><span class="p">[</span><span class="n">df</span><span class="p">[</span><span class="s1">&#39;x&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">]</span>
</pre></div>
</div>
<p>which could be replaced with <code class="docutils literal notranslate"><span class="pre">df[x=1]</span></code>.</p>
</li>
<li>xarray has named dimensions. Currently these are handled with functions .isel:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">data</span><span class="o">.</span><span class="n">isel</span><span class="p">(</span><span class="n">row</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># Returns the tenth row</span>
</pre></div>
</div>
<p>which could also be replaced with <code class="docutils literal notranslate"><span class="pre">data[row=10]</span></code>. A more complex example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="c1"># old syntax</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">isel</span><span class="p">(</span><span class="n">space</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="nb">slice</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="mi">2</span><span class="p">))[</span><span class="o">...</span><span class="p">]</span> <span class="o">=</span> <span class="n">spam</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># new syntax</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="p">[</span><span class="n">space</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">time</span><span class="o">=</span><span class="p">:</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">spam</span>
</pre></div>
</div>
<p>Another example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="c1"># old syntax</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">ds</span><span class="p">[</span><span class="s2">&quot;empty&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="nb">dict</span><span class="p">(</span><span class="n">lon</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">lat</span><span class="o">=</span><span class="mi">6</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">10</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># new syntax</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">ds</span><span class="p">[</span><span class="s2">&quot;empty&quot;</span><span class="p">][</span><span class="n">lon</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">lat</span><span class="o">=</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="mi">10</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># old syntax</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">ds</span><span class="p">[</span><span class="s2">&quot;empty&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="nb">dict</span><span class="p">(</span><span class="n">lon</span><span class="o">=</span><span class="nb">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="n">lat</span><span class="o">=</span><span class="nb">slice</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="kc">None</span><span class="p">))]</span> <span class="o">=</span> <span class="mi">10</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># new syntax</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">ds</span><span class="p">[</span><span class="s2">&quot;empty&quot;</span><span class="p">][</span><span class="n">lon</span><span class="o">=</span><span class="mi">1</span><span class="p">:</span><span class="mi">5</span><span class="p">,</span> <span class="n">lat</span><span class="o">=</span><span class="mi">6</span><span class="p">:]</span> <span class="o">=</span> <span class="mi">10</span>
</pre></div>
</div>
</li>
<li>Functions/methods whose argument is another function (plus its
arguments) need some way to determine which arguments are destined for
the target function, and which are used to configure how they run the
target. This is simple (if non-extensible) for positional parameters,
but we need some way to distinguish these for keywords. <a class="footnote-reference brackets" href="#trio-run" id="id4">[4]</a><p>An indexed notation would afford a Pythonic way to pass keyword
arguments to these functions without cluttering the callers code.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="c1"># Let&#39;s start this example with basic syntax without keywords.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># the positional values are arguments to `func` while</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># `name=` is processed by `trio.run`.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">trio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">value1</span><span class="p">,</span> <span class="n">value2</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;func&quot;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># `trio.run` ends up calling `func(value1, value2)`.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># If we want/need to pass value2 by keyword (keyword-only argument,</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># additional arguments that won&#39;t break backwards compatibility ...),</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># currently we need to resort to functools.partial:</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">trio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">functools</span><span class="o">.</span><span class="n">partial</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">param2</span><span class="o">=</span><span class="n">value2</span><span class="p">),</span> <span class="n">value1</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;func&quot;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">trio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">functools</span><span class="o">.</span><span class="n">partial</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">value1</span><span class="p">,</span> <span class="n">param2</span><span class="o">=</span><span class="n">value2</span><span class="p">),</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;func&quot;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># One possible workaround is to convert `trio.run` to an object</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># with a `__call__` method, and use an &quot;option&quot; helper,</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">trio</span><span class="o">.</span><span class="n">run</span><span class="o">.</span><span class="n">option</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;func&quot;</span><span class="p">)(</span><span class="n">func</span><span class="p">,</span> <span class="n">value1</span><span class="p">,</span> <span class="n">param2</span><span class="o">=</span><span class="n">value2</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># However, foo(bar)(baz) is uncommon and thus disruptive to the reader.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># Also, you need to remember the name of the `option` method.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># This PEP allows us to replace `option` with `__getitem__`.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c1"># The call is now shorter, more mnemonic, and looks+works like typing</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">trio</span><span class="o">.</span><span class="n">run</span><span class="p">[</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;func&quot;</span><span class="p">](</span><span class="n">func</span><span class="p">,</span> <span class="n">value1</span><span class="p">,</span> <span class="n">param2</span><span class="o">=</span><span class="n">value2</span><span class="p">)</span>
</pre></div>
</div>
</li>
<li>Availability of star arguments would benefit <a class="pep reference internal" href="../pep-0646/" title="PEP 646 Variadic Generics">PEP 646</a> Variadic Generics,
especially in the forms <code class="docutils literal notranslate"><span class="pre">a[*x]</span></code> and <code class="docutils literal notranslate"><span class="pre">a[*x,</span> <span class="pre">*y,</span> <span class="pre">p,</span> <span class="pre">q,</span> <span class="pre">*z]</span></code>. The PEP details
exactly this notation in its “Unpacking: Star Operator” section.</li>
</ol>
<p>It is important to note that how the notation is interpreted is up to the
implementation. This PEP only defines and dictates the behavior of Python
regarding passed keyword arguments, not how these arguments should be
interpreted and used by the implementing class.</p>
</section>
<section id="current-status-of-indexing-operation">
<h3><a class="toc-backref" href="#current-status-of-indexing-operation" role="doc-backlink">Current status of indexing operation</a></h3>
<p>Before detailing the new syntax and semantics to the indexing notation, it is
relevant to analyse how the indexing notation works today, in which contexts,
and how it is different from a function call.</p>
<p>Subscripting <code class="docutils literal notranslate"><span class="pre">obj[x]</span></code> is, effectively, an alternate and specialised form of
function call syntax with a number of differences and restrictions compared to
<code class="docutils literal notranslate"><span class="pre">obj(x)</span></code>. The current Python syntax focuses exclusively on position to express
the index, and also contains syntactic sugar to refer to non-punctiform
selection (slices). Some common examples:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="c1"># returns the fourth element of &#39;a&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="mi">10</span><span class="p">:</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># slice notation (extract a non-trivial data subset)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="c1"># multiple indexes (for multidimensional arrays)</span>
</pre></div>
</div>
<p>This translates into a <code class="docutils literal notranslate"><span class="pre">__(get|set|del)item__</span></code> dunder call which is passed a single
parameter containing the index (for <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> and <code class="docutils literal notranslate"><span class="pre">__delitem__</span></code>) or two parameters
containing index and value (for <code class="docutils literal notranslate"><span class="pre">__setitem__</span></code>).</p>
<p>The behavior of the indexing call is fundamentally different from a function call
in various aspects:</p>
<p>The first difference is in meaning to the reader. A function call says
“arbitrary function call potentially with side-effects”. An indexing operation
says “lookup”, typically to point at a subset or specific sub-aspect of an
entity (as in the case of typing notation). This fundamental difference means
that, while we cannot prevent abuse, implementors should be aware that the
introduction of keyword arguments to alter the behavior of the lookup may
violate this intrinsic meaning.</p>
<p>The second difference of the indexing notation compared to a function
is that indexing can be used for both getting and setting operations.
In Python, a function cannot be on the left hand side of an assignment. In
other words, both of these are valid:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
</pre></div>
</div>
<p>but only the first one of these is valid:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="mi">5</span> <span class="c1"># invalid</span>
</pre></div>
</div>
<p>This asymmetry is important, and makes one understand that there is a natural
imbalance between the two forms. It is therefore not a given that the two
should behave transparently and symmetrically.</p>
<p>The third difference is that functions have names assigned to their
arguments, unless the passed parameters are captured with <code class="docutils literal notranslate"><span class="pre">*args</span></code>, in which case
they end up as entries in the args tuple. In other words, functions already
have anonymous argument semantic, exactly like the indexing operation. However,
<code class="docutils literal notranslate"><span class="pre">__(get|set|del)item__</span></code> is not always receiving a tuple as the <code class="docutils literal notranslate"><span class="pre">index</span></code> argument
(to be uniform in behavior with <code class="docutils literal notranslate"><span class="pre">*args</span></code>). In fact, given a trivial class:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">X</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
</pre></div>
</div>
<p>The index operation basically forwards the content of the square brackets “as is”
in the <code class="docutils literal notranslate"><span class="pre">index</span></code> argument:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="o">=</span><span class="n">X</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="go">0</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="go">(0, 1)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span>
<span class="go">(0, 1)</span>
<span class="gp">&gt;&gt;&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[()]</span>
<span class="go">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">}]</span>
<span class="go">{1, 2, 3}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[</span><span class="s2">&quot;hello&quot;</span><span class="p">]</span>
<span class="go">hello</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[</span><span class="s2">&quot;hello&quot;</span><span class="p">,</span> <span class="s2">&quot;hi&quot;</span><span class="p">]</span>
<span class="go">(&#39;hello&#39;, &#39;hi&#39;)</span>
</pre></div>
</div>
<p>The fourth difference is that the indexing operation knows how to convert
colon notations to slices, thanks to support from the parser. This is valid:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="mi">3</span><span class="p">]</span>
</pre></div>
</div>
<p>this one isnt:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">:</span><span class="mi">3</span><span class="p">)</span>
</pre></div>
</div>
<p>The fifth difference is that theres no zero-argument form. This is valid:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">f</span><span class="p">()</span>
</pre></div>
</div>
<p>this one isnt:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span><span class="p">[]</span>
</pre></div>
</div>
</section>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>Before describing the specification, it is important to stress the difference in
nomenclature between <em>positional index</em>, <em>final index</em> and <em>keyword argument</em>, as it is important to
understand the fundamental asymmetries at play. The <code class="docutils literal notranslate"><span class="pre">__(get|set|del)item__</span></code>
is fundamentally an indexing operation, and the way the element is retrieved,
set, or deleted is through an index, the <em>final index</em>.</p>
<p>The current status quo is to directly build the <em>final index</em> from what is passed between
square brackets, the <em>positional index</em>. In other words, what is passed in the
square brackets is trivially used to generate what the code in <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> then uses
for the indicisation operation. As we already saw for the dict, <code class="docutils literal notranslate"><span class="pre">d[1]</span></code> has a
positional index of <code class="docutils literal notranslate"><span class="pre">1</span></code> and also a final index of <code class="docutils literal notranslate"><span class="pre">1</span></code> (because its the element that is
then added to the dictionary) and <code class="docutils literal notranslate"><span class="pre">d[1,</span> <span class="pre">2]</span></code> has positional index of <code class="docutils literal notranslate"><span class="pre">(1,</span> <span class="pre">2)</span></code> and
final index also of <code class="docutils literal notranslate"><span class="pre">(1,</span> <span class="pre">2)</span></code> (because yet again its the element that is added to the dictionary).
However, the positional index <code class="docutils literal notranslate"><span class="pre">d[1,2:3]</span></code> is not accepted by the dictionary, because
theres no way to transform the positional index into a final index, as the slice object is
unhashable. The positional index is what is currently known as the <code class="docutils literal notranslate"><span class="pre">index</span></code> parameter in
<code class="docutils literal notranslate"><span class="pre">__getitem__</span></code>. Nevertheless, nothing prevents to construct a dictionary-like class that
creates the final index by e.g. converting the positional index to a string.</p>
<p>This PEP extends the current status quo, and grants more flexibility to
create the final index via an enhanced syntax that combines the positional index
and keyword arguments, if passed.</p>
<p>The above brings an important point across. Keyword arguments, in the context of the index
operation, may be used to take indexing decisions to obtain the final index, and therefore
will have to accept values that are unconventional for functions. See for
example use case 1, where a slice is accepted.</p>
<p>The successful implementation of this PEP will result in the following behavior:</p>
<ol class="arabic">
<li>An empty subscript is still illegal, regardless of context (see Rejected Ideas):<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[]</span> <span class="c1"># SyntaxError</span>
</pre></div>
</div>
</li>
<li>A single index value remains a single index value when passed:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, index)</span>
<span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="c1"># calls type(obj).__setitem__(obj, index, value)</span>
<span class="k">del</span> <span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="c1"># calls type(obj).__delitem__(obj, index)</span>
</pre></div>
</div>
<p>This remains the case even if the index is followed by keywords; see point 5 below.</p>
</li>
<li>Comma-separated arguments are still parsed as a tuple and passed as
a single positional argument:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">spam</span><span class="p">,</span> <span class="n">eggs</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (spam, eggs))</span>
<span class="n">obj</span><span class="p">[</span><span class="n">spam</span><span class="p">,</span> <span class="n">eggs</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="c1"># calls type(obj).__setitem__(obj, (spam, eggs), value)</span>
<span class="k">del</span> <span class="n">obj</span><span class="p">[</span><span class="n">spam</span><span class="p">,</span> <span class="n">eggs</span><span class="p">]</span>
<span class="c1"># calls type(obj).__delitem__(obj, (spam, eggs))</span>
</pre></div>
</div>
<p>The points above mean that classes which do not want to support keyword
arguments in subscripts need do nothing at all, and the feature is therefore
completely backwards compatible.</p>
</li>
<li>Keyword arguments, if any, must follow positional arguments:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="c1"># SyntaxError</span>
</pre></div>
</div>
<p>This is like function calls, where intermixing positional and keyword
arguments give a SyntaxError.</p>
</li>
<li>Keyword subscripts, if any, will be handled like they are in
function calls. Examples:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Single index with keywords:</span>
<span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, index, spam=1, eggs=2)</span>
<span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="c1"># calls type(obj).__setitem__(obj, index, value, spam=1, eggs=2)</span>
<span class="k">del</span> <span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__delitem__(obj, index, spam=1, eggs=2)</span>
<span class="c1"># Comma-separated indices with keywords:</span>
<span class="n">obj</span><span class="p">[</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (foo, bar), spam=1, eggs=2)</span>
<span class="n">obj</span><span class="p">[</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="c1"># calls type(obj).__setitem__(obj, (foo, bar), value, spam=1, eggs=2)</span>
<span class="k">del</span> <span class="n">obj</span><span class="p">[</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__detitem__(obj, (foo, bar), spam=1, eggs=2)</span>
</pre></div>
</div>
<p>Note that:</p>
<ul class="simple">
<li>a single positional index will not turn into a tuple
just because one adds a keyword value.</li>
<li>for <code class="docutils literal notranslate"><span class="pre">__setitem__</span></code>, the same order is retained for index and value.
The keyword arguments go at the end, as is normal for a function
definition.</li>
</ul>
</li>
<li>The same rules apply with respect to keyword subscripts as for
keywords in function calls:<ul class="simple">
<li>the interpreter matches up each keyword subscript to a named parameter
in the appropriate method;</li>
<li>if a named parameter is used twice, that is an error;</li>
<li>if there are any named parameters left over (without a value) when the
keywords are all used, they are assigned their default value (if any);</li>
<li>if any such parameter doesnt have a default, that is an error;</li>
<li>if there are any keyword subscripts remaining after all the named
parameters are filled, and the method has a <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> parameter,
they are bound to the <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> parameter as a dict;</li>
<li>but if no <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code> parameter is defined, it is an error.</li>
</ul>
</li>
<li>Sequence unpacking is allowed inside subscripts:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="o">*</span><span class="n">items</span><span class="p">]</span>
</pre></div>
</div>
<p>This allows notations such as <code class="docutils literal notranslate"><span class="pre">[:,</span> <span class="pre">*args,</span> <span class="pre">:]</span></code>, which could be treated
as <code class="docutils literal notranslate"><span class="pre">[(slice(None),</span> <span class="pre">*args,</span> <span class="pre">slice(None))]</span></code>. Multiple star unpacking are
allowed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="o">*</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="o">*</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="mi">6</span><span class="p">,</span> <span class="n">foo</span><span class="o">=</span><span class="mi">5</span><span class="p">]</span>
<span class="c1"># Equivalent to obj[(1, 2, 3, 4, 5, 6), foo=3)</span>
</pre></div>
</div>
<p>The following notation equivalence must be honored:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="o">*</span><span class="p">()]</span>
<span class="c1"># Equivalent to obj[()]</span>
<span class="n">obj</span><span class="p">[</span><span class="o">*</span><span class="p">(),</span> <span class="n">foo</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># Equivalent to obj[(), foo=3]</span>
<span class="n">obj</span><span class="p">[</span><span class="o">*</span><span class="p">(</span><span class="n">x</span><span class="p">,)]</span>
<span class="c1"># Equivalent to obj[(x,)]</span>
<span class="n">obj</span><span class="p">[</span><span class="o">*</span><span class="p">(</span><span class="n">x</span><span class="p">,),]</span>
<span class="c1"># Equivalent to obj[(x,)]</span>
</pre></div>
</div>
<p>Note in particular case 3: sequence unpacking of a single element will
not behave as if only one single argument was passed. A related case is
the following example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="o">*</span><span class="p">(),</span> <span class="n">foo</span><span class="o">=</span><span class="mi">5</span><span class="p">]</span>
<span class="c1"># Equivalent to obj[(1,), foo=5]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1,), foo=5)</span>
</pre></div>
</div>
<p>However, as we saw earlier, for backward compatibility a single index will be passed as is:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="n">foo</span><span class="o">=</span><span class="mi">5</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, 1, foo=5)</span>
</pre></div>
</div>
<p>In other words, a single positional index will be passed “as is” only if no sequence
unpacking is present. If a sequence unpacking is present, then the index will become a tuple,
regardless of the resulting number of elements in the index after the unpacking has taken place.</p>
</li>
<li>Dict unpacking is permitted:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">items</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;spam&#39;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;eggs&#39;</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
<span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="p">,</span> <span class="o">**</span><span class="n">items</span><span class="p">]</span>
<span class="c1"># equivalent to obj[index, spam=1, eggs=2]</span>
</pre></div>
</div>
<p>The following notation equivalent should be honored:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="o">**</span><span class="p">{}]</span>
<span class="c1"># Equivalent to obj[()]</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="o">**</span><span class="p">{}]</span>
<span class="c1"># Equivalent to obj[3]</span>
</pre></div>
</div>
</li>
<li>Keyword-only subscripts are permitted. The positional index will be the empty tuple:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (), spam=1, eggs=2)</span>
<span class="n">obj</span><span class="p">[</span><span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
<span class="c1"># calls type(obj).__setitem__(obj, (), 5, spam=1, eggs=2)</span>
<span class="k">del</span> <span class="n">obj</span><span class="p">[</span><span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__delitem__(obj, (), spam=1, eggs=2)</span>
</pre></div>
</div>
<p>The choice of the empty tuple as a sentinel has been debated. Details are provided in
the Rejected Ideas section.</p>
</li>
<li>Keyword arguments must allow slice syntax:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">3</span><span class="p">:</span><span class="mi">4</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="mi">1</span><span class="p">:</span><span class="mi">4</span><span class="p">,</span> <span class="n">eggs</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, slice(3, 4, None), spam=slice(1, 4, None), eggs=2)</span>
</pre></div>
</div>
<p>This may open up the possibility to accept the same syntax for general function
calls, but this is not part of this recommendation.</p>
</li>
<li>Keyword arguments allow for default values:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Given type(obj).__getitem__(obj, index, spam=True, eggs=2)</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="c1"># Valid. index = 3, spam = True, eggs = 2</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="n">spam</span><span class="o">=</span><span class="kc">False</span><span class="p">]</span> <span class="c1"># Valid. index = 3, spam = False, eggs = 2</span>
<span class="n">obj</span><span class="p">[</span><span class="n">spam</span><span class="o">=</span><span class="kc">False</span><span class="p">]</span> <span class="c1"># Valid. index = (), spam = False, eggs = 2</span>
<span class="n">obj</span><span class="p">[]</span> <span class="c1"># Invalid.</span>
</pre></div>
</div>
</li>
<li>The same semantics given above must be extended to <code class="docutils literal notranslate"><span class="pre">__class__getitem__</span></code>:
Since <a class="pep reference internal" href="../pep-0560/" title="PEP 560 Core support for typing module and generic types">PEP 560</a>, type hints are dispatched so that for <code class="docutils literal notranslate"><span class="pre">x[y]</span></code>, if no
<code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> method is found, and <code class="docutils literal notranslate"><span class="pre">x</span></code> is a type (class) object,
and <code class="docutils literal notranslate"><span class="pre">x</span></code> has a class method <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code>, that method is
called. The same changes should be applied to this method as well,
so that a writing like <code class="docutils literal notranslate"><span class="pre">list[T=int]</span></code> can be accepted.</li>
</ol>
<section id="indexing-behavior-in-standard-classes-dict-list-etc">
<h3><a class="toc-backref" href="#indexing-behavior-in-standard-classes-dict-list-etc" role="doc-backlink">Indexing behavior in standard classes (dict, list, etc.)</a></h3>
<p>None of what is proposed in this PEP will change the behavior of the current
core classes that use indexing. Adding keywords to the index operation for
custom classes is not the same as modifying e.g. the standard dict type to
handle keyword arguments. In fact, dict (as well as list and other stdlib
classes with indexing semantics) will remain the same and will continue not to
accept keyword arguments. In other words, if <code class="docutils literal notranslate"><span class="pre">d</span></code> is a <code class="docutils literal notranslate"><span class="pre">dict</span></code>, the
statement <code class="docutils literal notranslate"><span class="pre">d[1,</span> <span class="pre">a=2]</span></code> will raise <code class="docutils literal notranslate"><span class="pre">TypeError</span></code>, as their implementation will
not support the use of keyword arguments. The same holds for all other classes
(list, dict, etc.)</p>
</section>
<section id="corner-case-and-gotchas">
<h3><a class="toc-backref" href="#corner-case-and-gotchas" role="doc-backlink">Corner case and Gotchas</a></h3>
<p>With the introduction of the new notation, a few corner cases need to be analysed.</p>
<ol class="arabic">
<li>Technically, if a class defines their getter like this:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">):</span>
</pre></div>
</div>
<p>then the caller could call that using keyword syntax, like these two cases:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="mi">4</span><span class="p">]</span>
<span class="n">obj</span><span class="p">[</span><span class="n">index</span><span class="o">=</span><span class="mi">1</span><span class="p">]</span>
</pre></div>
</div>
<p>The resulting behavior would be an error automatically, since it would be like
attempting to call the method with two values for the <code class="docutils literal notranslate"><span class="pre">index</span></code> argument, and
a <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> will be raised. In the first case, the <code class="docutils literal notranslate"><span class="pre">index</span></code> would be <code class="docutils literal notranslate"><span class="pre">3</span></code>,
in the second case, it would be the empty tuple <code class="docutils literal notranslate"><span class="pre">()</span></code>.</p>
<p>Note that this behavior applies for all currently existing classes that rely on
indexing, meaning that there is no way for the new behavior to introduce
backward compatibility issues on this respect.</p>
<p>Classes that wish to stress this behavior explicitly can define their
parameters as positional-only:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="o">/</span><span class="p">):</span>
</pre></div>
</div>
</li>
<li>a similar case occurs with setter notation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Given type(obj).__setitem__(obj, index, value):</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
</pre></div>
</div>
<p>This poses no issue because the value is passed automatically, and the Python interpreter will raise
<code class="docutils literal notranslate"><span class="pre">TypeError:</span> <span class="pre">got</span> <span class="pre">multiple</span> <span class="pre">values</span> <span class="pre">for</span> <span class="pre">keyword</span> <span class="pre">argument</span> <span class="pre">'value'</span></code></p>
</li>
<li>If the subscript dunders are declared to use positional-or-keyword
parameters, there may be some surprising cases when arguments are passed
to the method. Given the signature:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">direction</span><span class="o">=</span><span class="s1">&#39;north&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>if the caller uses this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;south&#39;</span><span class="p">]</span>
</pre></div>
</div>
<p>they will probably be surprised by the method call:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># expected type(obj).__getitem__(obj, 0, direction=&#39;south&#39;)</span>
<span class="c1"># but actually get:</span>
<span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;south&#39;</span><span class="p">),</span> <span class="n">direction</span><span class="o">=</span><span class="s1">&#39;north&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>Solution: best practice suggests that keyword subscripts should be
flagged as keyword-only when possible:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">direction</span><span class="o">=</span><span class="s1">&#39;north&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>The interpreter need not enforce this rule, as there could be scenarios
where this is the desired behaviour. But linters may choose to warn
about subscript methods which dont use the keyword-only flag.</p>
</li>
<li>As we saw, a single value followed by a keyword argument will not be changed into a tuple, i.e.:
<code class="docutils literal notranslate"><span class="pre">d[1,</span> <span class="pre">a=3]</span></code> is treated as <code class="docutils literal notranslate"><span class="pre">__getitem__(d,</span> <span class="pre">1,</span> <span class="pre">a=3)</span></code>, NOT <code class="docutils literal notranslate"><span class="pre">__getitem__(d,</span> <span class="pre">(1,),</span> <span class="pre">a=3)</span></code>. It would be
extremely confusing if adding keyword arguments were to change the type of the passed index.
In other words, adding a keyword to a single-valued subscript will not change it into a tuple.
For those cases where an actual tuple needs to be passed, a proper syntax will have to be used:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[(</span><span class="mi">1</span><span class="p">,),</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1,), a=3)</span>
</pre></div>
</div>
<p>In this case, the call is passing a single element (which is passed as is, as from rule above),
only that the single element happens to be a tuple.</p>
<p>Note that this behavior just reveals the truth that the <code class="docutils literal notranslate"><span class="pre">obj[1,]</span></code> notation is shorthand for
<code class="docutils literal notranslate"><span class="pre">obj[(1,)]</span></code> (and also <code class="docutils literal notranslate"><span class="pre">obj[1]</span></code> is shorthand for <code class="docutils literal notranslate"><span class="pre">obj[(1)]</span></code>, with the expected behavior).
When keywords are present, the rule that you can omit this outermost pair of parentheses is no
longer true:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, 1)</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, 1, a=3)</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1,))</span>
<span class="n">obj</span><span class="p">[(</span><span class="mi">1</span><span class="p">,),</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1,), a=3)</span>
</pre></div>
</div>
<p>This is particularly relevant in the case where two entries are passed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1, 2))</span>
<span class="n">obj</span><span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)]</span>
<span class="c1"># same as above</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1, 2), a=3)</span>
<span class="n">obj</span><span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1, 2), a=3)</span>
</pre></div>
</div>
<p>And particularly when the tuple is extracted as a variable:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">t</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">obj</span><span class="p">[</span><span class="n">t</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1, 2))</span>
<span class="n">obj</span><span class="p">[</span><span class="n">t</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, (1, 2), a=3)</span>
</pre></div>
</div>
<p>Why? because in the case <code class="docutils literal notranslate"><span class="pre">obj[1,</span> <span class="pre">2,</span> <span class="pre">a=3]</span></code> we are passing two elements (which
are then packed as a tuple and passed as the index). In the case <code class="docutils literal notranslate"><span class="pre">obj[(1,</span> <span class="pre">2),</span> <span class="pre">a=3]</span></code>
we are passing a single element (which is passed as is) which happens to be a tuple.
The final result is that they are the same.</p>
</li>
</ol>
</section>
</section>
<section id="c-interface">
<h2><a class="toc-backref" href="#c-interface" role="doc-backlink">C Interface</a></h2>
<p>Resolution of the indexing operation is performed through a call to the following functions</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">PyObject_GetItem(PyObject</span> <span class="pre">*o,</span> <span class="pre">PyObject</span> <span class="pre">*key)</span></code> for the get operation</li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject_SetItem(PyObject</span> <span class="pre">*o,</span> <span class="pre">PyObject</span> <span class="pre">*key,</span> <span class="pre">PyObject</span> <span class="pre">*value)</span></code> for the set operation</li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject_DelItem(PyObject</span> <span class="pre">*o,</span> <span class="pre">PyObject</span> <span class="pre">*key)</span></code> for the del operation</li>
</ul>
<p>These functions are used extensively within the Python executable, and are
also part of the public C API, as exported by <code class="docutils literal notranslate"><span class="pre">Include/abstract.h</span></code>. It is clear that
the signature of this function cannot be changed, and different C level functions
need to be implemented to support the extended call. We propose</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">PyObject_GetItemWithKeywords(PyObject</span> <span class="pre">*o,</span> <span class="pre">PyObject</span> <span class="pre">*key,</span> <span class="pre">PyObject</span> <span class="pre">*kwargs)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject_SetItemWithKeywords(PyObject</span> <span class="pre">*o,</span> <span class="pre">PyObject</span> <span class="pre">*key,</span> <span class="pre">PyObject</span> <span class="pre">*value,</span> <span class="pre">PyObject</span> <span class="pre">*kwargs)</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">PyObject_GetItemWithKeywords(PyObject</span> <span class="pre">*o,</span> <span class="pre">PyObject</span> <span class="pre">*key,</span> <span class="pre">PyObject</span> <span class="pre">*kwargs)</span></code></li>
</ul>
<p>New opcodes will be needed for the enhanced call. Currently, the
implementation uses <code class="docutils literal notranslate"><span class="pre">BINARY_SUBSCR</span></code>, <code class="docutils literal notranslate"><span class="pre">STORE_SUBSCR</span></code> and <code class="docutils literal notranslate"><span class="pre">DELETE_SUBSCR</span></code>
to invoke the old functions. We propose <code class="docutils literal notranslate"><span class="pre">BINARY_SUBSCR_KW</span></code>,
<code class="docutils literal notranslate"><span class="pre">STORE_SUBSCR_KW</span></code> and <code class="docutils literal notranslate"><span class="pre">DELETE_SUBSCR_KW</span></code> for the new operations. The
compiler will have to generate these new opcodes. The
old C implementations will call the extended methods passing <code class="docutils literal notranslate"><span class="pre">NULL</span></code>
as kwargs.</p>
<p>Finally, the following new slots must be added to the <code class="docutils literal notranslate"><span class="pre">PyMappingMethods</span></code> struct:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">mp_subscript_kw</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">mp_ass_subscript_kw</span></code></li>
</ul>
<p>These slots will have the appropriate signature to handle the dictionary object
containing the keywords.</p>
</section>
<section id="how-to-teach-recommendations">
<h2><a class="toc-backref" href="#how-to-teach-recommendations" role="doc-backlink">“How to teach” recommendations</a></h2>
<p>One request that occurred during feedback sessions was to detail a possible narrative
for teaching the feature, e.g. to students, data scientists, and similar audience.
This section addresses that need.</p>
<p>We will only describe the indexing from the perspective of use, not of
implementation, because it is the aspect that the above mentioned audience will
likely encounter. Only a subset of the users will have to implement their own
dunder functions, and can be considered advanced usage. A proper explanation could be:</p>
<blockquote>
<div>The indexing operation is generally used to refer to a subset of a larger
dataset by means of an index. In the commonly seen cases, the index is made by
one or more numbers, strings, slices, etc.<p>Some types may allow indexing to occur not only with the index, but also with
named values. These named values are given between square brackets using the
same syntax used for function call keyword arguments. The meaning of the names
and their use is found in the documentation of the type, as it varies from one
type to another.</p>
</div></blockquote>
<p>The teacher will now show some practical real world examples, explaining the
semantics of the feature in the shown library. At the time of writing these
examples do not exist, obviously, but the libraries most likely to implement
the feature are pandas and numpy, possibly as a method to refer to columns by
name.</p>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>A reference implementation is currently being developed here <a class="footnote-reference brackets" href="#reference-impl" id="id5">[6]</a>.</p>
</section>
<section id="workarounds">
<h2><a class="toc-backref" href="#workarounds" role="doc-backlink">Workarounds</a></h2>
<p>Every PEP that changes the Python language should <a class="pep reference internal" href="../pep-0001/#what-belongs-in-a-successful-pep" title="PEP 1 PEP Purpose and Guidelines § What belongs in a successful PEP?">“clearly explain why
the existing language specification is inadequate to address the
problem that the PEP solves”</a>.</p>
<p>Some rough equivalents to the proposed extension, which we call work-arounds,
are already possible. The work-arounds provide an alternative to enabling the
new syntax, while leaving the semantics to be defined elsewhere.</p>
<p>These work-arounds follow. In them the helpers <code class="docutils literal notranslate"><span class="pre">H</span></code> and <code class="docutils literal notranslate"><span class="pre">P</span></code> are not intended to
be universal. For example, a module or package might require the use of its own
helpers.</p>
<ol class="arabic">
<li>User defined classes can be given <code class="docutils literal notranslate"><span class="pre">getitem</span></code> and <code class="docutils literal notranslate"><span class="pre">delitem</span></code> methods,
that respectively get and delete values stored in a container:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">val</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="n">getitem</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="o">.</span><span class="n">delitem</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
<p>The same cant be done for <code class="docutils literal notranslate"><span class="pre">setitem</span></code>. Its not valid syntax:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="o">.</span><span class="n">setitem</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span> <span class="o">=</span> <span class="n">val</span>
<span class="go">SyntaxError: can&#39;t assign to function call</span>
</pre></div>
</div>
</li>
<li>A helper class, here called <code class="docutils literal notranslate"><span class="pre">H</span></code>, can be used to swap the container
and parameter roles. In other words, we use:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">H</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">)[</span><span class="n">x</span><span class="p">]</span>
</pre></div>
</div>
<p>as a substitute for:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">]</span>
</pre></div>
</div>
<p>This method will work for <code class="docutils literal notranslate"><span class="pre">getitem</span></code>, <code class="docutils literal notranslate"><span class="pre">delitem</span></code> and also for
<code class="docutils literal notranslate"><span class="pre">setitem</span></code>. This is because:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">H</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">)[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>
</pre></div>
</div>
<p>is valid syntax, which can be given the appropriate semantics.</p>
</li>
<li>A helper function, here called <code class="docutils literal notranslate"><span class="pre">P</span></code>, can be used to store the
arguments in a single object. For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="p">[</span><span class="n">P</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">4</span><span class="p">)]</span> <span class="o">=</span> <span class="n">val</span>
</pre></div>
</div>
<p>is valid syntax, and can be given the appropriate semantics.</p>
</li>
<li>The <code class="docutils literal notranslate"><span class="pre">lo:hi:step</span></code> syntax for slices is sometimes very useful. This
syntax is not directly available in the work-arounds. However:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">s</span><span class="p">[</span><span class="n">lo</span><span class="p">:</span><span class="n">hi</span><span class="p">:</span><span class="n">step</span><span class="p">]</span>
</pre></div>
</div>
<p>provides a work-around that is available everything, where:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">S</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span> <span class="k">return</span> <span class="n">key</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">S</span><span class="p">()</span>
</pre></div>
</div>
<p>defines the helper object <code class="docutils literal notranslate"><span class="pre">s</span></code>.</p>
</li>
</ol>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="previous-pep-472-solutions">
<h3><a class="toc-backref" href="#previous-pep-472-solutions" role="doc-backlink">Previous PEP 472 solutions</a></h3>
<p><a class="pep reference internal" href="../pep-0472/" title="PEP 472 Support for indexing with keyword arguments">PEP 472</a> presents a good amount of ideas that are now all to be considered
Rejected. A personal email from DAprano to the author specifically said:</p>
<blockquote>
<div>I have now carefully read through PEP 472 in full, and I am afraid I
cannot support any of the strategies currently in the PEP.</div></blockquote>
<p>We agree that those options are inferior to the currently presented, for one
reason or another.</p>
<p>To keep this document compact, we will not present here the objections for
all options presented in <a class="pep reference internal" href="../pep-0472/" title="PEP 472 Support for indexing with keyword arguments">PEP 472</a>. Suffice to say that they were discussed,
and each proposed alternative had one or few dealbreakers.</p>
</section>
<section id="adding-new-dunders">
<h3><a class="toc-backref" href="#adding-new-dunders" role="doc-backlink">Adding new dunders</a></h3>
<p>It was proposed to introduce new dunders <code class="docutils literal notranslate"><span class="pre">__(get|set|del)item_ex__</span></code>
that are invoked over the <code class="docutils literal notranslate"><span class="pre">__(get|set|del)item__</span></code> triad, if they are present.</p>
<p>The rationale around this choice is to make the intuition around how to add kwd
arg support to square brackets more obvious and in line with the function
behavior. Given:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">__getitem_ex__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>These all just work and produce the same result effortlessly:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">2</span><span class="p">]</span>
<span class="n">obj</span><span class="p">[</span><span class="n">y</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="mi">1</span><span class="p">]</span>
</pre></div>
</div>
<p>In other words, this solution would unify the behavior of <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> to the traditional
function signature, but since we cant change <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> and break backward compatibility,
we would have an extended version that is used preferentially.</p>
<p>The problems with this approach were found to be:</p>
<ul>
<li>It will slow down subscripting. For every subscript access, this new dunder
attribute gets investigated on the class, and if it is not present then the
default key translation function is executed.
Different ideas were proposed to handle this, from wrapping the method
only at class instantiation time, to add a bit flag to signal the availability
of these methods. Regardess of the solution, the new dunder would be effective
only if added at class creation time, not if its added later. This would
be unusual and would disallow (and behave unexpectedly) monkeypatching of the
methods for whatever reason it might be needed.</li>
<li>It adds complexity to the mechanism.</li>
<li>Will require a long and painful transition period during which time
libraries will have to somehow support both calling conventions, because most
likely, the extended methods will delegate to the traditional ones when the
right conditions are matched in the arguments, or some classes will support
the traditional dunder and others the extended dunder. While this will not
affect calling code, it will affect development.</li>
<li>it would potentially lead to mixed situations where the extended version is
defined for the getter, but not for the setter.</li>
<li>In the <code class="docutils literal notranslate"><span class="pre">__setitem_ex__</span></code> signature, value would have to be made the first
element, because the index is of arbitrary length depending on the specified
indexes. This would look awkward because the visual notation does not match
the signature:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span>
<span class="c1"># calls type(obj).__setitem_ex__(obj, 3, 1, 2)</span>
</pre></div>
</div>
</li>
<li>the solution relies on the assumption that all keyword indices necessarily map
into positional indices, or that they must have a name. This assumption may be
false: xarray, which is the primary Python package for numpy arrays with
labelled dimensions, supports indexing by additional dimensions (so called
“non-dimension coordinates”) that dont correspond directly to the dimensions
of the underlying numpy array, and those have no position to match up to.
In other words, anonymous indexes are a plausible use case that this solution
would remove, although it could be argued that using <code class="docutils literal notranslate"><span class="pre">*args</span></code> would solve
that issue.</li>
</ul>
</section>
<section id="adding-an-adapter-function">
<h3><a class="toc-backref" href="#adding-an-adapter-function" role="doc-backlink">Adding an adapter function</a></h3>
<p>Similar to the above, in the sense that a pre-function would be called to
convert the “new style” indexing into “old style indexing” that is then passed.
Has problems similar to the above.</p>
</section>
<section id="create-a-new-kwslice-object">
<h3><a class="toc-backref" href="#create-a-new-kwslice-object" role="doc-backlink">create a new “kwslice” object</a></h3>
<p>This proposal has already been explored in “New arguments contents” P4 in <a class="pep reference internal" href="../pep-0472/" title="PEP 472 Support for indexing with keyword arguments">PEP 472</a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span><span class="n">c</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="mi">1</span><span class="p">]</span>
<span class="c1"># calls type(obj).__getitem__(obj, a, slice(b, c), key(x=1))</span>
</pre></div>
</div>
<p>This solution requires everyone who needs keyword arguments to parse the tuple
and/or key object by hand to extract them. This is painful and opens up to the
get/set/del function to always accept arbitrary keyword arguments, whether they
make sense or not. We want the developer to be able to specify which arguments
make sense and which ones do not.</p>
</section>
<section id="using-a-single-bit-to-change-the-behavior">
<h3><a class="toc-backref" href="#using-a-single-bit-to-change-the-behavior" role="doc-backlink">Using a single bit to change the behavior</a></h3>
<p>A special class dunder flag:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">__keyfn__</span> <span class="o">=</span> <span class="kc">True</span>
</pre></div>
</div>
<p>would change the signature of the <code class="docutils literal notranslate"><span class="pre">__get|set|delitem__</span></code> to a “function like” dispatch,
meaning that this:</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="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">z</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
</pre></div>
</div>
<p>would result in a call to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">z</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="go"># instead of type(obj).__getitem__(obj, (1, 2), z=3)</span>
</pre></div>
</div>
<p>This option has been rejected because it feels odd that a signature of a method
depends on a specific value of another dunder. It would be confusing for both
static type checkers and for humans: a static type checker would have to hard-code
a special case for this, because there really is nothing else in Python
where the signature of a dunder depends on the value of another dunder.
A human that has to implement a <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> dunder would have to look if in the
class (or in any of its subclasses) for a <code class="docutils literal notranslate"><span class="pre">__keyfn__</span></code> before the dunder can be written.
Moreover, adding a base classes that have the <code class="docutils literal notranslate"><span class="pre">__keyfn__</span></code> flag set would break
the signature of the current methods. This would be even more problematic if the
flag is changed at runtime, or if the flag is generated by calling a function
that returns randomly True or something else.</p>
</section>
<section id="allowing-for-empty-index-notation-obj">
<h3><a class="toc-backref" href="#allowing-for-empty-index-notation-obj" role="doc-backlink">Allowing for empty index notation obj[]</a></h3>
<p>The current proposal prevents <code class="docutils literal notranslate"><span class="pre">obj[]</span></code> from being valid notation. However
a commenter stated</p>
<blockquote>
<div>We have <code class="docutils literal notranslate"><span class="pre">Tuple[int,</span> <span class="pre">int]</span></code> as a tuple of two integers. And we have <code class="docutils literal notranslate"><span class="pre">Tuple[int]</span></code>
as a tuple of one integer. And occasionally we need to spell a tuple of <em>no</em>
values, since thats the type of <code class="docutils literal notranslate"><span class="pre">()</span></code>. But we currently are forced to write
that as <code class="docutils literal notranslate"><span class="pre">Tuple[()]</span></code>. If we allowed <code class="docutils literal notranslate"><span class="pre">Tuple[]</span></code> that odd edge case would be
removed.<p>So I probably would be okay with allowing <code class="docutils literal notranslate"><span class="pre">obj[]</span></code> syntactically, as long as the
dict type could be made to reject it.</p>
</div></blockquote>
<p>This proposal already established that, in case no positional index is given, the
passed value must be the empty tuple. Allowing for the empty index notation would
make the dictionary type accept it automatically, to insert or refer to the value with
the empty tuple as key. Moreover, a typing notation such as <code class="docutils literal notranslate"><span class="pre">Tuple[]</span></code> can easily
be written as <code class="docutils literal notranslate"><span class="pre">Tuple</span></code> without the indexing notation.</p>
<p>However, subsequent discussion with Brandt Bucher during implementation has revealed
that the case <code class="docutils literal notranslate"><span class="pre">obj[]</span></code> would fit a natural evolution for variadic generics, giving
more strength to the above comment. In the end, after a discussion between DAprano,
Bucher and the author, we decided to leave the <code class="docutils literal notranslate"><span class="pre">obj[]</span></code> notation as a syntax
error for now, and possibly extend the notation with an additional PEP to hold
the equivalence <code class="docutils literal notranslate"><span class="pre">obj[]</span></code> as <code class="docutils literal notranslate"><span class="pre">obj[()]</span></code>.</p>
</section>
<section id="sentinel-value-for-no-given-positional-index">
<h3><a class="toc-backref" href="#sentinel-value-for-no-given-positional-index" role="doc-backlink">Sentinel value for no given positional index</a></h3>
<p>The topic of which value to pass as the index in the case of:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
</pre></div>
</div>
<p>has been considerably debated.</p>
<p>One apparently rational choice would be to pass no value at all, by making use of
the keyword only argument feature, but unfortunately will not work well with
the <code class="docutils literal notranslate"><span class="pre">__setitem__</span></code> dunder, as a positional element for the value is always
passed, and we cant “skip over” the index one unless we introduce a very weird behavior
where the first argument refers to the index when specified, and to the value when
index is not specified. This is extremely deceiving and error prone.</p>
<p>The above consideration makes it impossible to have a keyword only dunder, and
opens up the question of what entity to pass for the index position when no index
is passed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
<span class="c1"># would call type(obj).__setitem__(obj, ???, 5, k=3)</span>
</pre></div>
</div>
<p>A proposed hack would be to let the user specify which entity to use when an
index is not specified, by specifying a default for the <code class="docutils literal notranslate"><span class="pre">index</span></code>, but this
forces necessarily to also specify a (never going to be used, as a value is
always passed by design) default for the <code class="docutils literal notranslate"><span class="pre">value</span></code>, as we cant have
non-default arguments after defaulted one:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__setitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="n">SENTINEL</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="n">NEVERUSED</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
</pre></div>
</div>
<p>which seems ugly, redundant and confusing. We must therefore accept that some
form of sentinel index must be passed by the Python implementation when the
<code class="docutils literal notranslate"><span class="pre">obj[k=3]</span></code> notation is used. This also means that default arguments to those
parameters are simply never going to be used (but its already the
case with the current implementation, so no change there).</p>
<p>Additionally, some classes may want to use <code class="docutils literal notranslate"><span class="pre">**kwargs</span></code>, instead of a keyword-only
argument, meaning that having a definition like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="fm">__setitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</pre></div>
</div>
<p>and a user that wants to pass a keyword <code class="docutils literal notranslate"><span class="pre">value</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span><span class="p">[</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
</pre></div>
</div>
<p>expecting a call like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="fm">__setitem__</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">SENTINEL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">**</span><span class="p">{</span><span class="s2">&quot;value&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">})</span>
</pre></div>
</div>
<p>will instead accidentally be caught by the named <code class="docutils literal notranslate"><span class="pre">value</span></code>, producing a
<code class="docutils literal notranslate"><span class="pre">duplicate</span> <span class="pre">value</span> <span class="pre">error</span></code>. The user should not be worried about the actual
local names of those two arguments if they are, for all practical purposes,
positional only. Unfortunately, using positional-only values will ensure this
does not happen but it will still not solve the need to pass both <code class="docutils literal notranslate"><span class="pre">index</span></code> and
<code class="docutils literal notranslate"><span class="pre">value</span></code> even when the index is not provided. The point is that the user should not
be prevented to use keyword arguments to refer to a column <code class="docutils literal notranslate"><span class="pre">index</span></code>, <code class="docutils literal notranslate"><span class="pre">value</span></code>
(or <code class="docutils literal notranslate"><span class="pre">self</span></code>) just because the class implementor happens to use those names
in the parameter list.</p>
<p>Moreover, we also require the three dunders to behave in the same way: it would
be extremely inconvenient if only <code class="docutils literal notranslate"><span class="pre">__setitem__</span></code> were to receive this
sentinel, and <code class="docutils literal notranslate"><span class="pre">__get|delitem__</span></code> would not because they can get away with a
signature that allows for no index specification, thus allowing for a
user-specified default index.</p>
<p>Whatever the choice of the sentinel, it will make the following cases
degenerate and thus impossible to differentiate in the dunder:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="n">obj</span><span class="p">[</span><span class="n">SENTINEL</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
</pre></div>
</div>
<p>The question now shifts to which entity should represent the sentinel:
the options were:</p>
<ol class="arabic simple">
<li>Empty tuple</li>
<li>None</li>
<li>NotImplemented</li>
<li>a new sentinel object (e.g. NoIndex)</li>
</ol>
<p>For option 1, the call will become:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="p">(),</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
</pre></div>
</div>
<p>therefore making <code class="docutils literal notranslate"><span class="pre">obj[k=3]</span></code> and <code class="docutils literal notranslate"><span class="pre">obj[(),</span> <span class="pre">k=3]</span></code> degenerate and indistinguishable.</p>
<p>This option sounds appealing because:</p>
<ol class="arabic">
<li>The numpy community was inquired <a class="footnote-reference brackets" href="#numpy-ml" id="id6">[5]</a>, and the general consensus
of the responses was that the empty tuple felt appropriate.</li>
<li>It shows a parallel with the behavior of <code class="docutils literal notranslate"><span class="pre">*args</span></code> in a function, when
no positional arguments are given:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</span>
<span class="gp">...</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">foo</span><span class="p">(</span><span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="go">() {&#39;k&#39;: 3}</span>
</pre></div>
</div>
<p>Although we do accept the following asymmetry in behavior compared to functions
when a single value is passed, but that ship has sailed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">foo</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="go">(5,) {&#39;k&#39;: 3} # for indexing, a plain 5, not a 1-tuple is passed</span>
</pre></div>
</div>
</li>
</ol>
<p>For option 2, using <code class="docutils literal notranslate"><span class="pre">None</span></code>, it was objected that NumPy uses it to indicate
inserting a new axis/dimensions (theres a <code class="docutils literal notranslate"><span class="pre">np.newaxis</span></code> alias as well):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">arr</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="n">arr</span><span class="o">.</span><span class="n">ndim</span> <span class="o">==</span> <span class="mi">0</span>
<span class="n">arr</span><span class="p">[</span><span class="kc">None</span><span class="p">]</span><span class="o">.</span><span class="n">ndim</span> <span class="o">==</span> <span class="n">arr</span><span class="p">[</span><span class="kc">None</span><span class="p">,]</span><span class="o">.</span><span class="n">ndim</span> <span class="o">==</span> <span class="mi">1</span>
</pre></div>
</div>
<p>While this is not an insurmountable issue, it certainly will ripple onto numpy.</p>
<p>The only issues with both the above is that both the empty tuple and None are
potential legitimate indexes, and there might be value in being able to differentiate
the two degenerate cases.</p>
<p>So, an alternative strategy (option 3) would be to use an existing entity that is
unlikely to be used as a valid index. One option could be the current built-in constant
<code class="docutils literal notranslate"><span class="pre">NotImplemented</span></code>, which is currently returned by operators methods to
report that they do not implement a particular operation, and a different strategy
should be attempted (e.g. to ask the other object). Unfortunately, its name and
traditional use calls back to a feature that is not available, rather than the
fact that something was not passed by the user.</p>
<p>This leaves us with option 4: a new built-in constant. This constant
must be unhashable (so its never going to be a valid key) and have a clear
name that makes it obvious its context: <code class="docutils literal notranslate"><span class="pre">NoIndex</span></code>. This
would solve all the above issues, but the question is: is it worth it?</p>
<p>From a quick inquire, it seems that most people on python-ideas seem to believe
its not crucial, and the empty tuple is an acceptable option. Hence the
resulting series will be:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[</span><span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># type(obj).__getitem__(obj, (), k=3). Empty tuple</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># type(obj).__getitem__(obj, 1, k=3). Integer</span>
<span class="n">obj</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># type(obj).__getitem__(obj, (1, 2), k=3). Tuple</span>
</pre></div>
</div>
<p>and the following two notation will be degenerate:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">obj</span><span class="p">[(),</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># type(obj).__getitem__(obj, (), k=3)</span>
<span class="n">obj</span><span class="p">[</span><span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">]</span>
<span class="c1"># type(obj).__getitem__(obj, (), k=3)</span>
</pre></div>
</div>
</section>
</section>
<section id="common-objections">
<h2><a class="toc-backref" href="#common-objections" role="doc-backlink">Common objections</a></h2>
<ol class="arabic">
<li>Just use a method call.<p>One of the use cases is typing, where the indexing is used exclusively, and
function calls are out of the question. Moreover, function calls do not handle
slice notation, which is commonly used in some cases for arrays.</p>
<p>One problem is type hint creation has been extended to built-ins in Python 3.9,
so that you do not have to import Dict, List, et al anymore.</p>
<p>Without kwdargs inside <code class="docutils literal notranslate"><span class="pre">[]</span></code>, you would not be able to do this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Vector</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">[</span><span class="n">i</span><span class="o">=</span><span class="nb">float</span><span class="p">,</span> <span class="n">j</span><span class="o">=</span><span class="nb">float</span><span class="p">]</span>
</pre></div>
</div>
<p>but for obvious reasons, call syntax using builtins to create custom type hints
isnt an option:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">dict</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="nb">float</span><span class="p">,</span> <span class="n">j</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>
<span class="c1"># would create a dictionary, not a type</span>
</pre></div>
</div>
<p>Finally, function calls do not allow for a setitem-like notation, as shown
in the Overview: operations such as <code class="docutils literal notranslate"><span class="pre">f(1,</span> <span class="pre">x=3)</span> <span class="pre">=</span> <span class="pre">5</span></code> are not allowed, and are
instead allowed for indexing operations.</p>
</li>
</ol>
</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="rejection" role="doc-footnote">
<dt class="label" id="rejection">[<a href="#id1">1</a>]</dt>
<dd>“Rejection of PEP 472”
(<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2019-March/156693.html">https://mail.python.org/pipermail/python-dev/2019-March/156693.html</a>)</aside>
<aside class="footnote brackets" id="request-1" role="doc-footnote">
<dt class="label" id="request-1">[<a href="#id2">2</a>]</dt>
<dd>“Allow kwargs in __{get|set|del}item__”
(<a class="reference external" href="https://mail.python.org/archives/list/python-ideas&#64;python.org/thread/EUGDRTRFIY36K4RM3QRR52CKCI7MIR2M/">https://mail.python.org/archives/list/python-ideas&#64;python.org/thread/EUGDRTRFIY36K4RM3QRR52CKCI7MIR2M/</a>)</aside>
<aside class="footnote brackets" id="request-2" role="doc-footnote">
<dt class="label" id="request-2">[<a href="#id3">3</a>]</dt>
<dd>“PEP 472 Support for indexing with keyword arguments”
(<a class="reference external" href="https://mail.python.org/archives/list/python-ideas&#64;python.org/thread/6OGAFDWCXT5QVV23OZWKBY4TXGZBVYZS/">https://mail.python.org/archives/list/python-ideas&#64;python.org/thread/6OGAFDWCXT5QVV23OZWKBY4TXGZBVYZS/</a>)</aside>
<aside class="footnote brackets" id="trio-run" role="doc-footnote">
<dt class="label" id="trio-run">[<a href="#id4">4</a>]</dt>
<dd>“trio.run() should take **kwargs in addition to *args”
(<a class="reference external" href="https://github.com/python-trio/trio/issues/470">https://github.com/python-trio/trio/issues/470</a>)</aside>
<aside class="footnote brackets" id="numpy-ml" role="doc-footnote">
<dt class="label" id="numpy-ml">[<a href="#id6">5</a>]</dt>
<dd>“[Numpy-discussion] Request for comments on PEP 637 - Support for indexing with keyword arguments”
(<a class="reference external" href="http://numpy-discussion.10968.n7.nabble.com/Request-for-comments-on-PEP-637-Support-for-indexing-with-keyword-arguments-td48489.html">http://numpy-discussion.10968.n7.nabble.com/Request-for-comments-on-PEP-637-Support-for-indexing-with-keyword-arguments-td48489.html</a>)</aside>
<aside class="footnote brackets" id="reference-impl" role="doc-footnote">
<dt class="label" id="reference-impl">[<a href="#id5">6</a>]</dt>
<dd>“Reference implementation”
(<a class="reference external" href="https://github.com/python/cpython/compare/master...stefanoborini:PEP-637-implementation-attempt-2">https://github.com/python/cpython/compare/master…stefanoborini:PEP-637-implementation-attempt-2</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-0637.rst">https://github.com/python/peps/blob/main/peps/pep-0637.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0637.rst">2023-09-09 17:39:29 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="#overview">Overview</a><ul>
<li><a class="reference internal" href="#background">Background</a></li>
<li><a class="reference internal" href="#use-cases">Use cases</a></li>
<li><a class="reference internal" href="#current-status-of-indexing-operation">Current status of indexing operation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#indexing-behavior-in-standard-classes-dict-list-etc">Indexing behavior in standard classes (dict, list, etc.)</a></li>
<li><a class="reference internal" href="#corner-case-and-gotchas">Corner case and Gotchas</a></li>
</ul>
</li>
<li><a class="reference internal" href="#c-interface">C Interface</a></li>
<li><a class="reference internal" href="#how-to-teach-recommendations">“How to teach” recommendations</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#workarounds">Workarounds</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#previous-pep-472-solutions">Previous PEP 472 solutions</a></li>
<li><a class="reference internal" href="#adding-new-dunders">Adding new dunders</a></li>
<li><a class="reference internal" href="#adding-an-adapter-function">Adding an adapter function</a></li>
<li><a class="reference internal" href="#create-a-new-kwslice-object">create a new “kwslice” object</a></li>
<li><a class="reference internal" href="#using-a-single-bit-to-change-the-behavior">Using a single bit to change the behavior</a></li>
<li><a class="reference internal" href="#allowing-for-empty-index-notation-obj">Allowing for empty index notation obj[]</a></li>
<li><a class="reference internal" href="#sentinel-value-for-no-given-positional-index">Sentinel value for no given positional index</a></li>
</ul>
</li>
<li><a class="reference internal" href="#common-objections">Common objections</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-0637.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>