mirror of https://github.com/python/peps
873 lines
89 KiB
HTML
873 lines
89 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 586 – Literal Types | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0586/">
|
||
<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 586 – Literal Types | peps.python.org'>
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0586/">
|
||
<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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 586</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 586 – Literal Types</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Michael Lee <michael.lee.0x2a at gmail.com>, Ivan Levkivskyi <levkivskyi at gmail.com>, Jukka Lehtosalo <jukka.lehtosalo at iki.fi></dd>
|
||
<dt class="field-even">BDFL-Delegate<span class="colon">:</span></dt>
|
||
<dd class="field-even">Guido van Rossum <guido at python.org></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/typing-sig@python.org/">Typing-SIG list</a></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Topic<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="../topic/typing/">Typing</a></dd>
|
||
<dt class="field-odd">Created<span class="colon">:</span></dt>
|
||
<dd class="field-odd">14-Mar-2019</dd>
|
||
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-even">3.8</dd>
|
||
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-odd">14-Mar-2019</dd>
|
||
<dt class="field-even">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/typing-sig@python.org/message/FDO4KFYWYQEP3U2HVVBEBR3SXPHQSHYR/">Typing-SIG message</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation-and-rationale">Motivation and Rationale</a></li>
|
||
<li><a class="reference internal" href="#core-semantics">Core Semantics</a><ul>
|
||
<li><a class="reference internal" href="#core-behavior">Core behavior</a></li>
|
||
<li><a class="reference internal" href="#equivalence-of-two-literals">Equivalence of two Literals</a></li>
|
||
<li><a class="reference internal" href="#shortening-unions-of-literals">Shortening unions of literals</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#legal-and-illegal-parameterizations">Legal and illegal parameterizations</a><ul>
|
||
<li><a class="reference internal" href="#legal-parameters-for-literal-at-type-check-time">Legal parameters for <code class="docutils literal notranslate"><span class="pre">Literal</span></code> at type check time</a></li>
|
||
<li><a class="reference internal" href="#illegal-parameters-for-literal-at-type-check-time">Illegal parameters for <code class="docutils literal notranslate"><span class="pre">Literal</span></code> at type check time</a></li>
|
||
<li><a class="reference internal" href="#parameters-at-runtime">Parameters at runtime</a></li>
|
||
<li><a class="reference internal" href="#literals-enums-and-forward-references">Literals, enums, and forward references</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#type-inference">Type inference</a><ul>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards compatibility</a></li>
|
||
<li><a class="reference internal" href="#using-non-literals-in-literal-contexts">Using non-Literals in Literal contexts</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#interactions-with-other-types-and-features">Interactions with other types and features</a><ul>
|
||
<li><a class="reference internal" href="#intelligent-indexing-of-structured-data">Intelligent indexing of structured data</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-overloads">Interactions with overloads</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-generics">Interactions with generics</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-enums-and-exhaustiveness-checks">Interactions with enums and exhaustiveness checks</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-narrowing">Interactions with narrowing</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-final">Interactions with Final</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-or-out-of-scope-ideas">Rejected or out-of-scope ideas</a><ul>
|
||
<li><a class="reference internal" href="#true-dependent-types-integer-generics">True dependent types/integer generics</a></li>
|
||
<li><a class="reference internal" href="#adding-more-concise-syntax">Adding more concise syntax</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backporting-the-literal-type">Backporting the <code class="docutils literal notranslate"><span class="pre">Literal</span></code> type</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#related-work">Related work</a></li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<div class="pep-banner canonical-typing-spec sticky-banner admonition attention">
|
||
<p class="admonition-title">Attention</p>
|
||
<p>This PEP is a historical document. The up-to-date, canonical spec, <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/literal.html#literal-types" title="(in typing)"><span>Literals</span></a>, is maintained on the <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/">typing specs site</a>.</p>
|
||
<p class="close-button">×</p>
|
||
<p>See the <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/meta.html">typing specification update process</a> for how to propose changes.</p>
|
||
</div>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP proposes adding <em>Literal types</em> to the <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> ecosystem.
|
||
Literal types indicate that some expression has literally a
|
||
specific value. For example, the following function will accept
|
||
only expressions that have literally the value “4”:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Literal</span>
|
||
|
||
<span class="k">def</span> <span class="nf">accepts_only_four</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">4</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
|
||
<span class="n">accepts_only_four</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="c1"># OK</span>
|
||
<span class="n">accepts_only_four</span><span class="p">(</span><span class="mi">19</span><span class="p">)</span> <span class="c1"># Rejected</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="motivation-and-rationale">
|
||
<h2><a class="toc-backref" href="#motivation-and-rationale" role="doc-backlink">Motivation and Rationale</a></h2>
|
||
<p>Python has many APIs that return different types depending on the
|
||
value of some argument provided. For example:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">open(filename,</span> <span class="pre">mode)</span></code> returns either <code class="docutils literal notranslate"><span class="pre">IO[bytes]</span></code> or <code class="docutils literal notranslate"><span class="pre">IO[Text]</span></code>
|
||
depending on whether the second argument is something like <code class="docutils literal notranslate"><span class="pre">r</span></code> or
|
||
<code class="docutils literal notranslate"><span class="pre">rb</span></code>.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">subprocess.check_output(...)</span></code> returns either bytes or text
|
||
depending on whether the <code class="docutils literal notranslate"><span class="pre">universal_newlines</span></code> keyword argument is
|
||
set to <code class="docutils literal notranslate"><span class="pre">True</span></code> or not.</li>
|
||
</ul>
|
||
<p>This pattern is also fairly common in many popular 3rd party libraries.
|
||
For example, here are just two examples from pandas and numpy respectively:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">pandas.concat(...)</span></code> will return either <code class="docutils literal notranslate"><span class="pre">Series</span></code> or
|
||
<code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> depending on whether the <code class="docutils literal notranslate"><span class="pre">axis</span></code> argument is set to
|
||
0 or 1.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">numpy.unique</span></code> will return either a single array or a tuple containing
|
||
anywhere from two to four arrays depending on three boolean flag values.</li>
|
||
</ul>
|
||
<p>The typing issue tracker contains some
|
||
<a class="reference external" href="https://github.com/python/typing/issues/478">additional examples and discussion</a>.</p>
|
||
<p>There is currently no way of expressing the type signatures of these
|
||
functions: <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> does not include any mechanism for writing signatures
|
||
where the return type varies depending on the value passed in.
|
||
Note that this problem persists even if we redesign these APIs to
|
||
instead accept enums: <code class="docutils literal notranslate"><span class="pre">MyEnum.FOO</span></code> and <code class="docutils literal notranslate"><span class="pre">MyEnum.BAR</span></code> are both
|
||
considered to be of type <code class="docutils literal notranslate"><span class="pre">MyEnum</span></code>.</p>
|
||
<p>Currently, type checkers work around this limitation by adding ad hoc
|
||
extensions for important builtins and standard library functions. For
|
||
example, mypy comes bundled with a plugin that attempts to infer more
|
||
precise types for <code class="docutils literal notranslate"><span class="pre">open(...)</span></code>. While this approach works for standard
|
||
library functions, it’s unsustainable in general: it’s not reasonable to
|
||
expect 3rd party library authors to maintain plugins for N different
|
||
type checkers.</p>
|
||
<p>We propose adding <em>Literal types</em> to address these gaps.</p>
|
||
</section>
|
||
<section id="core-semantics">
|
||
<h2><a class="toc-backref" href="#core-semantics" role="doc-backlink">Core Semantics</a></h2>
|
||
<p>This section outlines the baseline behavior of literal types.</p>
|
||
<section id="core-behavior">
|
||
<h3><a class="toc-backref" href="#core-behavior" role="doc-backlink">Core behavior</a></h3>
|
||
<p>Literal types indicate that a variable has a specific and
|
||
concrete value. For example, if we define some variable <code class="docutils literal notranslate"><span class="pre">foo</span></code> to have
|
||
type <code class="docutils literal notranslate"><span class="pre">Literal[3]</span></code>, we are declaring that <code class="docutils literal notranslate"><span class="pre">foo</span></code> must be exactly equal
|
||
to <code class="docutils literal notranslate"><span class="pre">3</span></code> and no other value.</p>
|
||
<p>Given some value <code class="docutils literal notranslate"><span class="pre">v</span></code> that is a member of type <code class="docutils literal notranslate"><span class="pre">T</span></code>, the type
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[v]</span></code> shall be treated as a subtype of <code class="docutils literal notranslate"><span class="pre">T</span></code>. For example,
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[3]</span></code> is a subtype of <code class="docutils literal notranslate"><span class="pre">int</span></code>.</p>
|
||
<p>All methods from the parent type will be directly inherited by the
|
||
literal type. So, if we have some variable <code class="docutils literal notranslate"><span class="pre">foo</span></code> of type <code class="docutils literal notranslate"><span class="pre">Literal[3]</span></code>
|
||
it’s safe to do things like <code class="docutils literal notranslate"><span class="pre">foo</span> <span class="pre">+</span> <span class="pre">5</span></code> since <code class="docutils literal notranslate"><span class="pre">foo</span></code> inherits int’s
|
||
<code class="docutils literal notranslate"><span class="pre">__add__</span></code> method. The resulting type of <code class="docutils literal notranslate"><span class="pre">foo</span> <span class="pre">+</span> <span class="pre">5</span></code> is <code class="docutils literal notranslate"><span class="pre">int</span></code>.</p>
|
||
<p>This “inheriting” behavior is identical to how we
|
||
<a class="pep reference internal" href="../pep-0484/#newtype-helper-function" title="PEP 484 – Type Hints § NewType helper function">handle NewTypes</a>.</p>
|
||
</section>
|
||
<section id="equivalence-of-two-literals">
|
||
<h3><a class="toc-backref" href="#equivalence-of-two-literals" role="doc-backlink">Equivalence of two Literals</a></h3>
|
||
<p>Two types <code class="docutils literal notranslate"><span class="pre">Literal[v1]</span></code> and <code class="docutils literal notranslate"><span class="pre">Literal[v2]</span></code> are equivalent when
|
||
both of the following conditions are true:</p>
|
||
<ol class="arabic simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">type(v1)</span> <span class="pre">==</span> <span class="pre">type(v2)</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">v1</span> <span class="pre">==</span> <span class="pre">v2</span></code></li>
|
||
</ol>
|
||
<p>For example, <code class="docutils literal notranslate"><span class="pre">Literal[20]</span></code> and <code class="docutils literal notranslate"><span class="pre">Literal[0x14]</span></code> are equivalent.
|
||
However, <code class="docutils literal notranslate"><span class="pre">Literal[0]</span></code> and <code class="docutils literal notranslate"><span class="pre">Literal[False]</span></code> is <em>not</em> equivalent
|
||
despite that <code class="docutils literal notranslate"><span class="pre">0</span> <span class="pre">==</span> <span class="pre">False</span></code> evaluates to ‘true’ at runtime: <code class="docutils literal notranslate"><span class="pre">0</span></code>
|
||
has type <code class="docutils literal notranslate"><span class="pre">int</span></code> and <code class="docutils literal notranslate"><span class="pre">False</span></code> has type <code class="docutils literal notranslate"><span class="pre">bool</span></code>.</p>
|
||
</section>
|
||
<section id="shortening-unions-of-literals">
|
||
<h3><a class="toc-backref" href="#shortening-unions-of-literals" role="doc-backlink">Shortening unions of literals</a></h3>
|
||
<p>Literals are parameterized with one or more values. When a Literal is
|
||
parameterized with more than one value, it’s treated as exactly equivalent
|
||
to the union of those types. That is, <code class="docutils literal notranslate"><span class="pre">Literal[v1,</span> <span class="pre">v2,</span> <span class="pre">v3]</span></code> is equivalent
|
||
to <code class="docutils literal notranslate"><span class="pre">Union[Literal[v1],</span> <span class="pre">Literal[v2],</span> <span class="pre">Literal[v3]]</span></code>.</p>
|
||
<p>This shortcut helps make writing signatures for functions that accept
|
||
many different literals more ergonomic — for example, functions like
|
||
<code class="docutils literal notranslate"><span class="pre">open(...)</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Note: this is a simplification of the true type signature.</span>
|
||
<span class="n">_PathType</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">,</span> <span class="nb">int</span><span class="p">]</span>
|
||
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">_PathType</span><span class="p">,</span>
|
||
<span class="n">mode</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"r"</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">,</span> <span class="s2">"x"</span><span class="p">,</span> <span class="s2">"r+"</span><span class="p">,</span> <span class="s2">"w+"</span><span class="p">,</span> <span class="s2">"a+"</span><span class="p">,</span> <span class="s2">"x+"</span><span class="p">],</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">IO</span><span class="p">[</span><span class="n">Text</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">_PathType</span><span class="p">,</span>
|
||
<span class="n">mode</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"rb"</span><span class="p">,</span> <span class="s2">"wb"</span><span class="p">,</span> <span class="s2">"ab"</span><span class="p">,</span> <span class="s2">"xb"</span><span class="p">,</span> <span class="s2">"r+b"</span><span class="p">,</span> <span class="s2">"w+b"</span><span class="p">,</span> <span class="s2">"a+b"</span><span class="p">,</span> <span class="s2">"x+b"</span><span class="p">],</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">IO</span><span class="p">[</span><span class="nb">bytes</span><span class="p">]:</span> <span class="o">...</span>
|
||
|
||
<span class="c1"># Fallback overload for when the user isn't using literal types</span>
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">_PathType</span><span class="p">,</span> <span class="n">mode</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">IO</span><span class="p">[</span><span class="n">Any</span><span class="p">]:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The provided values do not all have to be members of the same type.
|
||
For example, <code class="docutils literal notranslate"><span class="pre">Literal[42,</span> <span class="pre">"foo",</span> <span class="pre">True]</span></code> is a legal type.</p>
|
||
<p>However, Literal <strong>must</strong> be parameterized with at least one type.
|
||
Types like <code class="docutils literal notranslate"><span class="pre">Literal[]</span></code> or <code class="docutils literal notranslate"><span class="pre">Literal</span></code> are illegal.</p>
|
||
</section>
|
||
</section>
|
||
<section id="legal-and-illegal-parameterizations">
|
||
<h2><a class="toc-backref" href="#legal-and-illegal-parameterizations" role="doc-backlink">Legal and illegal parameterizations</a></h2>
|
||
<p>This section describes what exactly constitutes a legal <code class="docutils literal notranslate"><span class="pre">Literal[...]</span></code> type:
|
||
what values may and may not be used as parameters.</p>
|
||
<p>In short, a <code class="docutils literal notranslate"><span class="pre">Literal[...]</span></code> type may be parameterized by one or more literal
|
||
expressions, and nothing else.</p>
|
||
<section id="legal-parameters-for-literal-at-type-check-time">
|
||
<h3><a class="toc-backref" href="#legal-parameters-for-literal-at-type-check-time" role="doc-backlink">Legal parameters for <code class="docutils literal notranslate"><span class="pre">Literal</span></code> at type check time</a></h3>
|
||
<p><code class="docutils literal notranslate"><span class="pre">Literal</span></code> may be parameterized with literal ints, byte and unicode strings,
|
||
bools, Enum values and <code class="docutils literal notranslate"><span class="pre">None</span></code>. So for example, all of
|
||
the following would be legal:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Literal</span><span class="p">[</span><span class="mi">26</span><span class="p">]</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="mh">0x1A</span><span class="p">]</span> <span class="c1"># Exactly equivalent to Literal[26]</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">]</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="s2">"hello world"</span><span class="p">]</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="sa">b</span><span class="s2">"hello world"</span><span class="p">]</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="sa">u</span><span class="s2">"hello world"</span><span class="p">]</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="kc">True</span><span class="p">]</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="n">Color</span><span class="o">.</span><span class="n">RED</span><span class="p">]</span> <span class="c1"># Assuming Color is some enum</span>
|
||
<span class="n">Literal</span><span class="p">[</span><span class="kc">None</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><strong>Note:</strong> Since the type <code class="docutils literal notranslate"><span class="pre">None</span></code> is inhabited by just a single
|
||
value, the types <code class="docutils literal notranslate"><span class="pre">None</span></code> and <code class="docutils literal notranslate"><span class="pre">Literal[None]</span></code> are exactly equivalent.
|
||
Type checkers may simplify <code class="docutils literal notranslate"><span class="pre">Literal[None]</span></code> into just <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">Literal</span></code> may also be parameterized by other literal types, or type aliases
|
||
to other literal types. For example, the following is legal:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ReadOnlyMode</span> <span class="o">=</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"r"</span><span class="p">,</span> <span class="s2">"r+"</span><span class="p">]</span>
|
||
<span class="n">WriteAndTruncateMode</span> <span class="o">=</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"w"</span><span class="p">,</span> <span class="s2">"w+"</span><span class="p">,</span> <span class="s2">"wt"</span><span class="p">,</span> <span class="s2">"w+t"</span><span class="p">]</span>
|
||
<span class="n">WriteNoTruncateMode</span> <span class="o">=</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"r+"</span><span class="p">,</span> <span class="s2">"r+t"</span><span class="p">]</span>
|
||
<span class="n">AppendMode</span> <span class="o">=</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"a"</span><span class="p">,</span> <span class="s2">"a+"</span><span class="p">,</span> <span class="s2">"at"</span><span class="p">,</span> <span class="s2">"a+t"</span><span class="p">]</span>
|
||
|
||
<span class="n">AllModes</span> <span class="o">=</span> <span class="n">Literal</span><span class="p">[</span><span class="n">ReadOnlyMode</span><span class="p">,</span> <span class="n">WriteAndTruncateMode</span><span class="p">,</span>
|
||
<span class="n">WriteNoTruncateMode</span><span class="p">,</span> <span class="n">AppendMode</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This feature is again intended to help make using and reusing literal types
|
||
more ergonomic.</p>
|
||
<p><strong>Note:</strong> As a consequence of the above rules, type checkers are also expected
|
||
to support types that look like the following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Literal</span><span class="p">[</span><span class="n">Literal</span><span class="p">[</span><span class="n">Literal</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="s2">"foo"</span><span class="p">],</span> <span class="mi">5</span><span class="p">,</span> <span class="kc">None</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This should be exactly equivalent to the following type:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Literal</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="s2">"foo"</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="kc">None</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>…and also to the following type:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Optional</span><span class="p">[</span><span class="n">Literal</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="s2">"foo"</span><span class="p">,</span> <span class="mi">5</span><span class="p">]]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><strong>Note:</strong> String literal types like <code class="docutils literal notranslate"><span class="pre">Literal["foo"]</span></code> should subtype either
|
||
bytes or unicode in the same way regular string literals do at runtime.</p>
|
||
<p>For example, in Python 3, the type <code class="docutils literal notranslate"><span class="pre">Literal["foo"]</span></code> is equivalent to
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[u"foo"]</span></code>, since <code class="docutils literal notranslate"><span class="pre">"foo"</span></code> is equivalent to <code class="docutils literal notranslate"><span class="pre">u"foo"</span></code> in Python 3.</p>
|
||
<p>Similarly, in Python 2, the type <code class="docutils literal notranslate"><span class="pre">Literal["foo"]</span></code> is equivalent to
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[b"foo"]</span></code> – unless the file includes a
|
||
<code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">unicode_literals</span></code> import, in which case it would be
|
||
equivalent to <code class="docutils literal notranslate"><span class="pre">Literal[u"foo"]</span></code>.</p>
|
||
</section>
|
||
<section id="illegal-parameters-for-literal-at-type-check-time">
|
||
<h3><a class="toc-backref" href="#illegal-parameters-for-literal-at-type-check-time" role="doc-backlink">Illegal parameters for <code class="docutils literal notranslate"><span class="pre">Literal</span></code> at type check time</a></h3>
|
||
<p>The following parameters are intentionally disallowed by design:</p>
|
||
<ul class="simple">
|
||
<li>Arbitrary expressions like <code class="docutils literal notranslate"><span class="pre">Literal[3</span> <span class="pre">+</span> <span class="pre">4]</span></code> or
|
||
<code class="docutils literal notranslate"><span class="pre">Literal["foo".replace("o",</span> <span class="pre">"b")]</span></code>.<ul>
|
||
<li>Rationale: Literal types are meant to be a
|
||
minimal extension to the <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> typing ecosystem and requiring type
|
||
checkers to interpret potentially expressions inside types adds too
|
||
much complexity. Also see <a class="reference internal" href="#rejected-or-out-of-scope-ideas">Rejected or out-of-scope ideas</a>.</li>
|
||
<li>As a consequence, complex numbers like <code class="docutils literal notranslate"><span class="pre">Literal[4</span> <span class="pre">+</span> <span class="pre">3j]</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[-4</span> <span class="pre">+</span> <span class="pre">2j]</span></code> are also prohibited. For consistency, literals like
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[4j]</span></code> that contain just a single complex number are also
|
||
prohibited.</li>
|
||
<li>The only exception to this rule is the unary <code class="docutils literal notranslate"><span class="pre">-</span></code> (minus) for ints: types
|
||
like <code class="docutils literal notranslate"><span class="pre">Literal[-5]</span></code> are <em>accepted</em>.</li>
|
||
</ul>
|
||
</li>
|
||
<li>Tuples containing valid literal types like <code class="docutils literal notranslate"><span class="pre">Literal[(1,</span> <span class="pre">"foo",</span> <span class="pre">"bar")]</span></code>.
|
||
The user could always express this type as
|
||
<code class="docutils literal notranslate"><span class="pre">Tuple[Literal[1],</span> <span class="pre">Literal["foo"],</span> <span class="pre">Literal["bar"]]</span></code> instead. Also,
|
||
tuples are likely to be confused with the <code class="docutils literal notranslate"><span class="pre">Literal[1,</span> <span class="pre">2,</span> <span class="pre">3]</span></code>
|
||
shortcut.</li>
|
||
<li>Mutable literal data structures like dict literals, list literals, or
|
||
set literals: literals are always implicitly final and immutable. So,
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[{"a":</span> <span class="pre">"b",</span> <span class="pre">"c":</span> <span class="pre">"d"}]</span></code> is illegal.</li>
|
||
<li>Any other types: for example, <code class="docutils literal notranslate"><span class="pre">Literal[Path]</span></code>, or
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[some_object_instance]</span></code> are illegal. This includes typevars: if
|
||
<code class="docutils literal notranslate"><span class="pre">T</span></code> is a typevar, <code class="docutils literal notranslate"><span class="pre">Literal[T]</span></code> is not allowed. Typevars can vary over
|
||
only types, never over values.</li>
|
||
</ul>
|
||
<p>The following are provisionally disallowed for simplicity. We can consider
|
||
allowing them in future extensions of this PEP.</p>
|
||
<ul class="simple">
|
||
<li>Floats: e.g. <code class="docutils literal notranslate"><span class="pre">Literal[3.14]</span></code>. Representing Literals of infinity or NaN
|
||
in a clean way is tricky; real-world APIs are unlikely to vary their
|
||
behavior based on a float parameter.</li>
|
||
<li>Any: e.g. <code class="docutils literal notranslate"><span class="pre">Literal[Any]</span></code>. <code class="docutils literal notranslate"><span class="pre">Any</span></code> is a type, and <code class="docutils literal notranslate"><span class="pre">Literal[...]</span></code> is
|
||
meant to contain values only. It is also unclear what <code class="docutils literal notranslate"><span class="pre">Literal[Any]</span></code>
|
||
would actually semantically mean.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="parameters-at-runtime">
|
||
<h3><a class="toc-backref" href="#parameters-at-runtime" role="doc-backlink">Parameters at runtime</a></h3>
|
||
<p>Although the set of parameters <code class="docutils literal notranslate"><span class="pre">Literal[...]</span></code> may contain at type check time
|
||
is very small, the actual implementation of <code class="docutils literal notranslate"><span class="pre">typing.Literal</span></code> will not perform
|
||
any checks at runtime. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">my_function</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span><span class="p">])</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">3</span>
|
||
|
||
<span class="n">x</span><span class="p">:</span> <span class="n">Literal</span> <span class="o">=</span> <span class="mi">3</span>
|
||
<span class="n">y</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="n">my_function</span><span class="p">]</span> <span class="o">=</span> <span class="n">my_function</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The type checker should reject this program: all three uses of
|
||
<code class="docutils literal notranslate"><span class="pre">Literal</span></code> are <em>invalid</em> according to this spec. However, Python itself
|
||
should execute this program with no errors.</p>
|
||
<p>This is partly to help us preserve flexibility in case we want to expand the
|
||
scope of what <code class="docutils literal notranslate"><span class="pre">Literal</span></code> can be used for in the future, and partly because
|
||
it is not possible to detect all illegal parameters at runtime to begin with.
|
||
For example, it is impossible to distinguish between <code class="docutils literal notranslate"><span class="pre">Literal[1</span> <span class="pre">+</span> <span class="pre">2]</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[3]</span></code> at runtime.</p>
|
||
</section>
|
||
<section id="literals-enums-and-forward-references">
|
||
<h3><a class="toc-backref" href="#literals-enums-and-forward-references" role="doc-backlink">Literals, enums, and forward references</a></h3>
|
||
<p>One potential ambiguity is between literal strings and forward
|
||
references to literal enum members. For example, suppose we have the
|
||
type <code class="docutils literal notranslate"><span class="pre">Literal["Color.RED"]</span></code>. Does this literal type
|
||
contain a string literal or a forward reference to some <code class="docutils literal notranslate"><span class="pre">Color.RED</span></code>
|
||
enum member?</p>
|
||
<p>In cases like these, we always assume the user meant to construct a
|
||
literal string. If the user wants a forward reference, they must wrap
|
||
the entire literal type in a string – e.g. <code class="docutils literal notranslate"><span class="pre">"Literal[Color.RED]"</span></code>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="type-inference">
|
||
<h2><a class="toc-backref" href="#type-inference" role="doc-backlink">Type inference</a></h2>
|
||
<p>This section describes a few rules regarding type inference and
|
||
literals, along with some examples.</p>
|
||
<section id="backwards-compatibility">
|
||
<h3><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards compatibility</a></h3>
|
||
<p>When type checkers add support for Literal, it’s important they do so
|
||
in a way that maximizes backwards-compatibility. Type checkers should
|
||
ensure that code that used to type check continues to do so after support
|
||
for Literal is added on a best-effort basis.</p>
|
||
<p>This is particularly important when performing type inference. For
|
||
example, given the statement <code class="docutils literal notranslate"><span class="pre">x</span> <span class="pre">=</span> <span class="pre">"blue"</span></code>, should the inferred
|
||
type of <code class="docutils literal notranslate"><span class="pre">x</span></code> be <code class="docutils literal notranslate"><span class="pre">str</span></code> or <code class="docutils literal notranslate"><span class="pre">Literal["blue"]</span></code>?</p>
|
||
<p>One naive strategy would be to always assume expressions are intended
|
||
to be Literal types. So, <code class="docutils literal notranslate"><span class="pre">x</span></code> would always have an inferred type of
|
||
<code class="docutils literal notranslate"><span class="pre">Literal["blue"]</span></code> in the example above. This naive strategy is almost
|
||
certainly too disruptive – it would cause programs like the following
|
||
to start failing when they previously did not:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># If a type checker infers 'var' has type Literal[3]</span>
|
||
<span class="c1"># and my_list has type List[Literal[3]]...</span>
|
||
<span class="n">var</span> <span class="o">=</span> <span class="mi">3</span>
|
||
<span class="n">my_list</span> <span class="o">=</span> <span class="p">[</span><span class="n">var</span><span class="p">]</span>
|
||
|
||
<span class="c1"># ...this call would be a type-error.</span>
|
||
<span class="n">my_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Another example of when this strategy would fail is when setting fields
|
||
in objects:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">MyObject</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="c1"># If a type checker infers MyObject.field has type Literal[3]...</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">field</span> <span class="o">=</span> <span class="mi">3</span>
|
||
|
||
<span class="n">m</span> <span class="o">=</span> <span class="n">MyObject</span><span class="p">()</span>
|
||
|
||
<span class="c1"># ...this assignment would no longer type check</span>
|
||
<span class="n">m</span><span class="o">.</span><span class="n">field</span> <span class="o">=</span> <span class="mi">4</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>An alternative strategy that <em>does</em> maintain compatibility in every case would
|
||
be to always assume expressions are <em>not</em> Literal types unless they are
|
||
explicitly annotated otherwise. A type checker using this strategy would
|
||
always infer that <code class="docutils literal notranslate"><span class="pre">x</span></code> is of type <code class="docutils literal notranslate"><span class="pre">str</span></code> in the first example above.</p>
|
||
<p>This is not the only viable strategy: type checkers should feel free to experiment
|
||
with more sophisticated inference techniques. This PEP does not mandate any
|
||
particular strategy; it only emphasizes the importance of backwards compatibility.</p>
|
||
</section>
|
||
<section id="using-non-literals-in-literal-contexts">
|
||
<h3><a class="toc-backref" href="#using-non-literals-in-literal-contexts" role="doc-backlink">Using non-Literals in Literal contexts</a></h3>
|
||
<p>Literal types follow the existing rules regarding subtyping with no additional
|
||
special-casing. For example, programs like the following are type safe:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">expects_str</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="n">var</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"foo"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"foo"</span>
|
||
|
||
<span class="c1"># Legal: Literal["foo"] is a subtype of str</span>
|
||
<span class="n">expects_str</span><span class="p">(</span><span class="n">var</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This also means non-Literal expressions in general should not automatically
|
||
be cast to Literal. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">expects_literal</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"foo"</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">runner</span><span class="p">(</span><span class="n">my_str</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="c1"># ILLEGAL: str is not a subclass of Literal["foo"]</span>
|
||
<span class="n">expects_literal</span><span class="p">(</span><span class="n">my_str</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><strong>Note:</strong> If the user wants their API to support accepting both literals
|
||
<em>and</em> the original type – perhaps for legacy purposes – they should
|
||
implement a fallback overload. See <a class="reference internal" href="#interactions-with-overloads">Interactions with overloads</a>.</p>
|
||
</section>
|
||
</section>
|
||
<section id="interactions-with-other-types-and-features">
|
||
<h2><a class="toc-backref" href="#interactions-with-other-types-and-features" role="doc-backlink">Interactions with other types and features</a></h2>
|
||
<p>This section discusses how Literal types interact with other existing types.</p>
|
||
<section id="intelligent-indexing-of-structured-data">
|
||
<h3><a class="toc-backref" href="#intelligent-indexing-of-structured-data" role="doc-backlink">Intelligent indexing of structured data</a></h3>
|
||
<p>Literals can be used to “intelligently index” into structured types like
|
||
tuples, NamedTuple, and classes. (Note: this is not an exhaustive list).</p>
|
||
<p>For example, type checkers should infer the correct value type when
|
||
indexing into a tuple using an int key that corresponds a valid index:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">a</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="n">b</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span>
|
||
|
||
<span class="n">some_tuple</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">,</span> <span class="n">List</span><span class="p">[</span><span class="nb">bool</span><span class="p">]]</span> <span class="o">=</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s2">"abc"</span><span class="p">,</span> <span class="p">[</span><span class="kc">True</span><span class="p">,</span> <span class="kc">False</span><span class="p">])</span>
|
||
<span class="n">reveal_type</span><span class="p">(</span><span class="n">some_tuple</span><span class="p">[</span><span class="n">a</span><span class="p">])</span> <span class="c1"># Revealed type is 'int'</span>
|
||
<span class="n">some_tuple</span><span class="p">[</span><span class="n">b</span><span class="p">]</span> <span class="c1"># Error: 5 is not a valid index into the tuple</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We expect similar behavior when using functions like getattr:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Test</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">param</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">myfield</span> <span class="o">=</span> <span class="n">param</span>
|
||
|
||
<span class="k">def</span> <span class="nf">mymethod</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">val</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="n">a</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"myfield"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"myfield"</span>
|
||
<span class="n">b</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"mymethod"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"mymethod"</span>
|
||
<span class="n">c</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"blah"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"blah"</span>
|
||
|
||
<span class="n">t</span> <span class="o">=</span> <span class="n">Test</span><span class="p">()</span>
|
||
<span class="n">reveal_type</span><span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">a</span><span class="p">))</span> <span class="c1"># Revealed type is 'int'</span>
|
||
<span class="n">reveal_type</span><span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">b</span><span class="p">))</span> <span class="c1"># Revealed type is 'Callable[[int], str]'</span>
|
||
<span class="nb">getattr</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> <span class="c1"># Error: No attribute named 'blah' in Test</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><strong>Note:</strong> See <a class="reference internal" href="#interactions-with-final">Interactions with Final</a> for a proposal on how we can
|
||
express the variable declarations above in a more compact manner.</p>
|
||
</section>
|
||
<section id="interactions-with-overloads">
|
||
<h3><a class="toc-backref" href="#interactions-with-overloads" role="doc-backlink">Interactions with overloads</a></h3>
|
||
<p>Literal types and overloads do not need to interact in a special
|
||
way: the existing rules work fine.</p>
|
||
<p>However, one important use case type checkers must take care to
|
||
support is the ability to use a <em>fallback</em> when the user is not using literal
|
||
types. For example, consider <code class="docutils literal notranslate"><span class="pre">open</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_PathType</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">,</span> <span class="nb">int</span><span class="p">]</span>
|
||
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">_PathType</span><span class="p">,</span>
|
||
<span class="n">mode</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"r"</span><span class="p">,</span> <span class="s2">"w"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">,</span> <span class="s2">"x"</span><span class="p">,</span> <span class="s2">"r+"</span><span class="p">,</span> <span class="s2">"w+"</span><span class="p">,</span> <span class="s2">"a+"</span><span class="p">,</span> <span class="s2">"x+"</span><span class="p">],</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">IO</span><span class="p">[</span><span class="n">Text</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">_PathType</span><span class="p">,</span>
|
||
<span class="n">mode</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"rb"</span><span class="p">,</span> <span class="s2">"wb"</span><span class="p">,</span> <span class="s2">"ab"</span><span class="p">,</span> <span class="s2">"xb"</span><span class="p">,</span> <span class="s2">"r+b"</span><span class="p">,</span> <span class="s2">"w+b"</span><span class="p">,</span> <span class="s2">"a+b"</span><span class="p">,</span> <span class="s2">"x+b"</span><span class="p">],</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">IO</span><span class="p">[</span><span class="nb">bytes</span><span class="p">]:</span> <span class="o">...</span>
|
||
|
||
<span class="c1"># Fallback overload for when the user isn't using literal types</span>
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">_PathType</span><span class="p">,</span> <span class="n">mode</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">IO</span><span class="p">[</span><span class="n">Any</span><span class="p">]:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If we were to change the signature of <code class="docutils literal notranslate"><span class="pre">open</span></code> to use just the first two overloads,
|
||
we would break any code that does not pass in a literal string expression.
|
||
For example, code like this would be broken:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mode</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">pick_file_mode</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
|
||
<span class="c1"># f should continue to be of type IO[Any] here</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A little more broadly: we propose adding a policy to typeshed that
|
||
mandates that whenever we add literal types to some existing API, we also
|
||
always include a fallback overload to maintain backwards-compatibility.</p>
|
||
</section>
|
||
<section id="interactions-with-generics">
|
||
<h3><a class="toc-backref" href="#interactions-with-generics" role="doc-backlink">Interactions with generics</a></h3>
|
||
<p>Types like <code class="docutils literal notranslate"><span class="pre">Literal[3]</span></code> are meant to be just plain old subclasses of
|
||
<code class="docutils literal notranslate"><span class="pre">int</span></code>. This means you can use types like <code class="docutils literal notranslate"><span class="pre">Literal[3]</span></code> anywhere
|
||
you could use normal types, such as with generics.</p>
|
||
<p>This means that it is legal to parameterize generic functions or
|
||
classes using Literal types:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">A</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'A'</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
|
||
<span class="n">B</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'B'</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
|
||
<span class="n">C</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'C'</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
|
||
|
||
<span class="c1"># A simplified definition for Matrix[row, column]</span>
|
||
<span class="k">class</span> <span class="nc">Matrix</span><span class="p">(</span><span class="n">Generic</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="k">def</span> <span class="fm">__add__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">:</span> <span class="n">Matrix</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="o">-></span> <span class="n">Matrix</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="o">...</span>
|
||
<span class="k">def</span> <span class="fm">__matmul__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">:</span> <span class="n">Matrix</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="o">-></span> <span class="n">Matrix</span><span class="p">[</span><span class="n">A</span><span class="p">,</span> <span class="n">C</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">transpose</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Matrix</span><span class="p">[</span><span class="n">B</span><span class="p">,</span> <span class="n">A</span><span class="p">]:</span> <span class="o">...</span>
|
||
|
||
<span class="n">foo</span><span class="p">:</span> <span class="n">Matrix</span><span class="p">[</span><span class="n">Literal</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">3</span><span class="p">]]</span> <span class="o">=</span> <span class="n">Matrix</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||
<span class="n">bar</span><span class="p">:</span> <span class="n">Matrix</span><span class="p">[</span><span class="n">Literal</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">7</span><span class="p">]]</span> <span class="o">=</span> <span class="n">Matrix</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||
|
||
<span class="n">baz</span> <span class="o">=</span> <span class="n">foo</span> <span class="o">@</span> <span class="n">bar</span>
|
||
<span class="n">reveal_type</span><span class="p">(</span><span class="n">baz</span><span class="p">)</span> <span class="c1"># Revealed type is 'Matrix[Literal[2], Literal[7]]'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Similarly, it is legal to construct TypeVars with value restrictions
|
||
or bounds involving Literal types:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">,</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"a"</span><span class="p">],</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"b"</span><span class="p">],</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">"c"</span><span class="p">])</span>
|
||
<span class="n">S</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'S'</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="n">Literal</span><span class="p">[</span><span class="s2">"foo"</span><span class="p">])</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>…although it is unclear when it would ever be useful to construct a
|
||
TypeVar with a Literal upper bound. For example, the <code class="docutils literal notranslate"><span class="pre">S</span></code> TypeVar in
|
||
the above example is essentially pointless: we can get equivalent behavior
|
||
by using <code class="docutils literal notranslate"><span class="pre">S</span> <span class="pre">=</span> <span class="pre">Literal["foo"]</span></code> instead.</p>
|
||
<p><strong>Note:</strong> Literal types and generics deliberately interact in only very
|
||
basic and limited ways. In particular, libraries that want to type check
|
||
code containing a heavy amount of numeric or numpy-style manipulation will
|
||
almost certainly likely find Literal types as proposed in this PEP to be
|
||
insufficient for their needs.</p>
|
||
<p>We considered several different proposals for fixing this, but ultimately
|
||
decided to defer the problem of integer generics to a later date. See
|
||
<a class="reference internal" href="#rejected-or-out-of-scope-ideas">Rejected or out-of-scope ideas</a> for more details.</p>
|
||
</section>
|
||
<section id="interactions-with-enums-and-exhaustiveness-checks">
|
||
<h3><a class="toc-backref" href="#interactions-with-enums-and-exhaustiveness-checks" role="doc-backlink">Interactions with enums and exhaustiveness checks</a></h3>
|
||
<p>Type checkers should be capable of performing exhaustiveness checks when
|
||
working Literal types that have a closed number of variants, such as
|
||
enums. For example, the type checker should be capable of inferring that
|
||
the final <code class="docutils literal notranslate"><span class="pre">else</span></code> statement must be of type <code class="docutils literal notranslate"><span class="pre">str</span></code>, since all three
|
||
values of the <code class="docutils literal notranslate"><span class="pre">Status</span></code> enum have already been exhausted:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Status</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||
<span class="n">SUCCESS</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="n">INVALID_DATA</span> <span class="o">=</span> <span class="mi">1</span>
|
||
<span class="n">FATAL_ERROR</span> <span class="o">=</span> <span class="mi">2</span>
|
||
|
||
<span class="k">def</span> <span class="nf">parse_status</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Status</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="n">s</span> <span class="ow">is</span> <span class="n">Status</span><span class="o">.</span><span class="n">SUCCESS</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Success!"</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="n">s</span> <span class="ow">is</span> <span class="n">Status</span><span class="o">.</span><span class="n">INVALID_DATA</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"The given data is invalid because..."</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="n">s</span> <span class="ow">is</span> <span class="n">Status</span><span class="o">.</span><span class="n">FATAL_ERROR</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Unexpected fatal error..."</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># 's' must be of type 'str' since all other options are exhausted</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="s2">"Got custom status: "</span> <span class="o">+</span> <span class="n">s</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The interaction described above is not new: it’s already
|
||
<a class="pep reference internal" href="../pep-0484/#support-for-singleton-types-in-unions" title="PEP 484 – Type Hints § Support for singleton types in unions">codified within PEP 484</a>.
|
||
However, many type
|
||
checkers (such as mypy) do not yet implement this due to the expected
|
||
complexity of the implementation work.</p>
|
||
<p>Some of this complexity will be alleviated once Literal types are introduced:
|
||
rather than entirely special-casing enums, we can instead treat them as being
|
||
approximately equivalent to the union of their values and take advantage of any
|
||
existing logic regarding unions, exhaustibility, type narrowing, reachability,
|
||
and so forth the type checker might have already implemented.</p>
|
||
<p>So here, the <code class="docutils literal notranslate"><span class="pre">Status</span></code> enum could be treated as being approximately equivalent
|
||
to <code class="docutils literal notranslate"><span class="pre">Literal[Status.SUCCESS,</span> <span class="pre">Status.INVALID_DATA,</span> <span class="pre">Status.FATAL_ERROR]</span></code>
|
||
and the type of <code class="docutils literal notranslate"><span class="pre">s</span></code> narrowed accordingly.</p>
|
||
</section>
|
||
<section id="interactions-with-narrowing">
|
||
<h3><a class="toc-backref" href="#interactions-with-narrowing" role="doc-backlink">Interactions with narrowing</a></h3>
|
||
<p>Type checkers may optionally perform additional analysis for both enum and
|
||
non-enum Literal types beyond what is described in the section above.</p>
|
||
<p>For example, it may be useful to perform narrowing based on things like
|
||
containment or equality checks:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">parse_status</span><span class="p">(</span><span class="n">status</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="n">status</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"MALFORMED"</span><span class="p">,</span> <span class="s2">"ABORTED"</span><span class="p">):</span>
|
||
<span class="c1"># Type checker could narrow 'status' to type</span>
|
||
<span class="c1"># Literal["MALFORMED", "ABORTED"] here.</span>
|
||
<span class="k">return</span> <span class="n">expects_bad_status</span><span class="p">(</span><span class="n">status</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Similarly, type checker could narrow 'status' to Literal["PENDING"]</span>
|
||
<span class="k">if</span> <span class="n">status</span> <span class="o">==</span> <span class="s2">"PENDING"</span><span class="p">:</span>
|
||
<span class="n">expects_pending_status</span><span class="p">(</span><span class="n">status</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>It may also be useful to perform narrowing taking into account expressions
|
||
involving Literal bools. For example, we can combine <code class="docutils literal notranslate"><span class="pre">Literal[True]</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[False]</span></code>, and overloads to construct “custom type guards”:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">is_int_like</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]])</span> <span class="o">-></span> <span class="n">Literal</span><span class="p">[</span><span class="kc">True</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="nf">is_int_like</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">object</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">is_int_like</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="o">...</span>
|
||
|
||
<span class="n">vector</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</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="mi">3</span><span class="p">]</span>
|
||
<span class="k">if</span> <span class="n">is_int_like</span><span class="p">(</span><span class="n">vector</span><span class="p">):</span>
|
||
<span class="n">vector</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">vector</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"bad"</span><span class="p">)</span> <span class="c1"># This branch is inferred to be unreachable</span>
|
||
|
||
<span class="n">scalar</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span>
|
||
<span class="k">if</span> <span class="n">is_int_like</span><span class="p">(</span><span class="n">scalar</span><span class="p">):</span>
|
||
<span class="n">scalar</span> <span class="o">+=</span> <span class="mi">3</span> <span class="c1"># Type checks: type of 'scalar' is narrowed to 'int'</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">scalar</span> <span class="o">+=</span> <span class="s2">"foo"</span> <span class="c1"># Type checks: type of 'scalar' is narrowed to 'str'</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="interactions-with-final">
|
||
<h3><a class="toc-backref" href="#interactions-with-final" role="doc-backlink">Interactions with Final</a></h3>
|
||
<p><a class="pep reference internal" href="../pep-0591/" title="PEP 591 – Adding a final qualifier to typing">PEP 591</a> proposes adding a “Final” qualifier to the typing
|
||
ecosystem. This qualifier can be used to declare that some variable or
|
||
attribute cannot be reassigned:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">foo</span><span class="p">:</span> <span class="n">Final</span> <span class="o">=</span> <span class="mi">3</span>
|
||
<span class="n">foo</span> <span class="o">=</span> <span class="mi">4</span> <span class="c1"># Error: 'foo' is declared to be Final</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that in the example above, we know that <code class="docutils literal notranslate"><span class="pre">foo</span></code> will always be equal to
|
||
exactly <code class="docutils literal notranslate"><span class="pre">3</span></code>. A type checker can use this information to deduce that <code class="docutils literal notranslate"><span class="pre">foo</span></code>
|
||
is valid to use in any context that expects a <code class="docutils literal notranslate"><span class="pre">Literal[3]</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">expects_three</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="n">expects_three</span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> <span class="c1"># Type checks, since 'foo' is Final and equal to 3</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">Final</span></code> qualifier serves as a shorthand for declaring that a variable
|
||
is <em>effectively Literal</em>.</p>
|
||
<p>If both this PEP and <a class="pep reference internal" href="../pep-0591/" title="PEP 591 – Adding a final qualifier to typing">PEP 591</a> are accepted, type checkers are expected to
|
||
support this shortcut. Specifically, given a variable or attribute assignment
|
||
of the form <code class="docutils literal notranslate"><span class="pre">var:</span> <span class="pre">Final</span> <span class="pre">=</span> <span class="pre">value</span></code> where <code class="docutils literal notranslate"><span class="pre">value</span></code> is a valid parameter for
|
||
<code class="docutils literal notranslate"><span class="pre">Literal[...]</span></code>, type checkers should understand that <code class="docutils literal notranslate"><span class="pre">var</span></code> may be used in
|
||
any context that expects a <code class="docutils literal notranslate"><span class="pre">Literal[value]</span></code>.</p>
|
||
<p>Type checkers are not obligated to understand any other uses of Final. For
|
||
example, whether or not the following program type checks is left unspecified:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Note: The assignment does not exactly match the form 'var: Final = value'.</span>
|
||
<span class="n">bar1</span><span class="p">:</span> <span class="n">Final</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="mi">3</span>
|
||
<span class="n">expects_three</span><span class="p">(</span><span class="n">bar1</span><span class="p">)</span> <span class="c1"># May or may not be accepted by type checkers</span>
|
||
|
||
<span class="c1"># Note: "Literal[1 + 2]" is not a legal type.</span>
|
||
<span class="n">bar2</span><span class="p">:</span> <span class="n">Final</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
|
||
<span class="n">expects_three</span><span class="p">(</span><span class="n">bar2</span><span class="p">)</span> <span class="c1"># May or may not be accepted by type checkers</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="rejected-or-out-of-scope-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-or-out-of-scope-ideas" role="doc-backlink">Rejected or out-of-scope ideas</a></h2>
|
||
<p>This section outlines some potential features that are explicitly out-of-scope.</p>
|
||
<section id="true-dependent-types-integer-generics">
|
||
<h3><a class="toc-backref" href="#true-dependent-types-integer-generics" role="doc-backlink">True dependent types/integer generics</a></h3>
|
||
<p>This proposal is essentially describing adding a very simplified
|
||
dependent type system to the <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> ecosystem. One obvious extension
|
||
would be to implement a full-fledged dependent type system that lets users
|
||
predicate types based on their values in arbitrary ways. That would
|
||
let us write signatures like the below:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># A vector has length 'n', containing elements of type 'T'</span>
|
||
<span class="k">class</span> <span class="nc">Vector</span><span class="p">(</span><span class="n">Generic</span><span class="p">[</span><span class="n">N</span><span class="p">,</span> <span class="n">T</span><span class="p">]):</span> <span class="o">...</span>
|
||
|
||
<span class="c1"># The type checker will statically verify our function genuinely does</span>
|
||
<span class="c1"># construct a vector that is equal in length to "len(vec1) + len(vec2)"</span>
|
||
<span class="c1"># and will throw an error if it does not.</span>
|
||
<span class="k">def</span> <span class="nf">concat</span><span class="p">(</span><span class="n">vec1</span><span class="p">:</span> <span class="n">Vector</span><span class="p">[</span><span class="n">A</span><span class="p">,</span> <span class="n">T</span><span class="p">],</span> <span class="n">vec2</span><span class="p">:</span> <span class="n">Vector</span><span class="p">[</span><span class="n">B</span><span class="p">,</span> <span class="n">T</span><span class="p">])</span> <span class="o">-></span> <span class="n">Vector</span><span class="p">[</span><span class="n">A</span> <span class="o">+</span> <span class="n">B</span><span class="p">,</span> <span class="n">T</span><span class="p">]:</span>
|
||
<span class="c1"># ...snip...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>At the very least, it would be useful to add some form of integer generics.</p>
|
||
<p>Although such a type system would certainly be useful, it’s out of scope
|
||
for this PEP: it would require a far more substantial amount of implementation
|
||
work, discussion, and research to complete compared to the current proposal.</p>
|
||
<p>It’s entirely possible we’ll circle back and revisit this topic in the future:
|
||
we very likely will need some form of dependent typing along with other
|
||
extensions like variadic generics to support popular libraries like numpy.</p>
|
||
<p>This PEP should be seen as a stepping stone towards this goal,
|
||
rather than an attempt at providing a comprehensive solution.</p>
|
||
</section>
|
||
<section id="adding-more-concise-syntax">
|
||
<h3><a class="toc-backref" href="#adding-more-concise-syntax" role="doc-backlink">Adding more concise syntax</a></h3>
|
||
<p>One objection to this PEP is that having to explicitly write <code class="docutils literal notranslate"><span class="pre">Literal[...]</span></code>
|
||
feels verbose. For example, instead of writing:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foobar</span><span class="p">(</span><span class="n">arg1</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">arg2</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="kc">True</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>…it would be nice to instead write:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foobar</span><span class="p">(</span><span class="n">arg1</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">:</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Unfortunately, these abbreviations simply will not work with the
|
||
existing implementation of <code class="docutils literal notranslate"><span class="pre">typing</span></code> at runtime. For example, the
|
||
following snippet crashes when run using Python 3.7:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Tuple</span>
|
||
|
||
<span class="c1"># Supposed to accept tuple containing the literals 1 and 2</span>
|
||
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Tuple</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="kc">None</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Running this yields the following exception:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="ne">TypeError</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">t0</span><span class="p">,</span> <span class="n">t1</span><span class="p">,</span> <span class="o">...</span><span class="p">]:</span> <span class="n">each</span> <span class="n">t</span> <span class="n">must</span> <span class="n">be</span> <span class="n">a</span> <span class="nb">type</span><span class="o">.</span> <span class="n">Got</span> <span class="mf">1.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We don’t want users to have to memorize exactly when it’s ok to elide
|
||
<code class="docutils literal notranslate"><span class="pre">Literal</span></code>, so we require <code class="docutils literal notranslate"><span class="pre">Literal</span></code> to always be present.</p>
|
||
<p>A little more broadly, we feel overhauling the syntax of types in
|
||
Python is not within the scope of this PEP: it would be best to have
|
||
that discussion in a separate PEP, instead of attaching it to this one.
|
||
So, this PEP deliberately does not try and innovate Python’s type syntax.</p>
|
||
</section>
|
||
</section>
|
||
<section id="backporting-the-literal-type">
|
||
<h2><a class="toc-backref" href="#backporting-the-literal-type" role="doc-backlink">Backporting the <code class="docutils literal notranslate"><span class="pre">Literal</span></code> type</a></h2>
|
||
<p>Once this PEP is accepted, the <code class="docutils literal notranslate"><span class="pre">Literal</span></code> type will need to be backported for
|
||
Python versions that come bundled with older versions of the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module.
|
||
We plan to do this by adding <code class="docutils literal notranslate"><span class="pre">Literal</span></code> to the <code class="docutils literal notranslate"><span class="pre">typing_extensions</span></code> 3rd party
|
||
module, which contains a variety of other backported types.</p>
|
||
</section>
|
||
<section id="implementation">
|
||
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
|
||
<p>The mypy type checker currently has implemented a large subset of the behavior
|
||
described in this spec, with the exception of enum Literals and some of the
|
||
more complex narrowing interactions described above.</p>
|
||
</section>
|
||
<section id="related-work">
|
||
<h2><a class="toc-backref" href="#related-work" role="doc-backlink">Related work</a></h2>
|
||
<p>This proposal was written based on the discussion that took place in the
|
||
following threads:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="https://github.com/python/typing/issues/478">Check that literals belong to/are excluded from a set of values</a></li>
|
||
<li><a class="reference external" href="https://github.com/python/mypy/issues/3062">Simple dependent types</a></li>
|
||
<li><a class="reference external" href="https://github.com/python/typing/issues/513">Typing for multi-dimensional arrays</a></li>
|
||
</ul>
|
||
<p>The overall design of this proposal also ended up converging into
|
||
something similar to how
|
||
<a class="reference external" href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal_types">literal types are handled in TypeScript</a>.</p>
|
||
</section>
|
||
<section id="acknowledgements">
|
||
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
|
||
<p>Thanks to Mark Mendoza, Ran Benita, Rebecca Chen, and the other members of
|
||
typing-sig for their comments on this PEP.</p>
|
||
<p>Additional thanks to the various participants in the mypy and typing issue
|
||
trackers, who helped provide a lot of the motivation and reasoning behind
|
||
this PEP.</p>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document has been placed in the public domain.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0586.rst">https://github.com/python/peps/blob/main/peps/pep-0586.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0586.rst">2024-02-14 20:29:34 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation-and-rationale">Motivation and Rationale</a></li>
|
||
<li><a class="reference internal" href="#core-semantics">Core Semantics</a><ul>
|
||
<li><a class="reference internal" href="#core-behavior">Core behavior</a></li>
|
||
<li><a class="reference internal" href="#equivalence-of-two-literals">Equivalence of two Literals</a></li>
|
||
<li><a class="reference internal" href="#shortening-unions-of-literals">Shortening unions of literals</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#legal-and-illegal-parameterizations">Legal and illegal parameterizations</a><ul>
|
||
<li><a class="reference internal" href="#legal-parameters-for-literal-at-type-check-time">Legal parameters for <code class="docutils literal notranslate"><span class="pre">Literal</span></code> at type check time</a></li>
|
||
<li><a class="reference internal" href="#illegal-parameters-for-literal-at-type-check-time">Illegal parameters for <code class="docutils literal notranslate"><span class="pre">Literal</span></code> at type check time</a></li>
|
||
<li><a class="reference internal" href="#parameters-at-runtime">Parameters at runtime</a></li>
|
||
<li><a class="reference internal" href="#literals-enums-and-forward-references">Literals, enums, and forward references</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#type-inference">Type inference</a><ul>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards compatibility</a></li>
|
||
<li><a class="reference internal" href="#using-non-literals-in-literal-contexts">Using non-Literals in Literal contexts</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#interactions-with-other-types-and-features">Interactions with other types and features</a><ul>
|
||
<li><a class="reference internal" href="#intelligent-indexing-of-structured-data">Intelligent indexing of structured data</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-overloads">Interactions with overloads</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-generics">Interactions with generics</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-enums-and-exhaustiveness-checks">Interactions with enums and exhaustiveness checks</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-narrowing">Interactions with narrowing</a></li>
|
||
<li><a class="reference internal" href="#interactions-with-final">Interactions with Final</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-or-out-of-scope-ideas">Rejected or out-of-scope ideas</a><ul>
|
||
<li><a class="reference internal" href="#true-dependent-types-integer-generics">True dependent types/integer generics</a></li>
|
||
<li><a class="reference internal" href="#adding-more-concise-syntax">Adding more concise syntax</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backporting-the-literal-type">Backporting the <code class="docutils literal notranslate"><span class="pre">Literal</span></code> type</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#related-work">Related work</a></li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0586.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> |