python-peps/pep-0585/index.html

493 lines
41 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 585 Type Hinting Generics In Standard Collections | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0585/">
<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 585 Type Hinting Generics In Standard Collections | peps.python.org'>
<meta property="og:description" content="Static typing as defined by PEPs 484, 526, 544, 560, and 563 was built incrementally on top of the existing Python runtime and constrained by existing syntax and runtime behavior. This led to the existence of a duplicated collection hierarchy in the ty...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0585/">
<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="Static typing as defined by PEPs 484, 526, 544, 560, and 563 was built incrementally on top of the existing Python runtime and constrained by existing syntax and runtime behavior. This led to the existence of a duplicated collection hierarchy in the ty...">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 585</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 585 Type Hinting Generics In Standard Collections</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Łukasz Langa &lt;lukasz&#32;&#97;t&#32;python.org&gt;</dd>
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/">Typing-SIG list</a></dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-even">Type<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-odd">Topic<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="../topic/typing/">Typing</a></dd>
<dt class="field-even">Created<span class="colon">:</span></dt>
<dd class="field-even">03-Mar-2019</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.9</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/python-dev&#64;python.org/thread/HW2NFOEMCVCTAFLBLC3V7MLM6ZNMKP42/">Python-Dev thread</a></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#rationale-and-goals">Rationale and Goals</a></li>
<li><a class="reference internal" href="#terminology">Terminology</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards compatibility</a></li>
<li><a class="reference internal" href="#implementation">Implementation</a><ul>
<li><a class="reference internal" href="#parameters-to-generics-are-available-at-runtime">Parameters to generics are available at runtime</a></li>
<li><a class="reference internal" href="#forward-compatibility">Forward compatibility</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference implementation</a></li>
<li><a class="reference internal" href="#rejected-alternatives">Rejected alternatives</a><ul>
<li><a class="reference internal" href="#do-nothing">Do nothing</a></li>
<li><a class="reference internal" href="#generics-erasure">Generics erasure</a></li>
<li><a class="reference internal" href="#disallowing-instantiation-of-parameterized-types">Disallowing instantiation of parameterized types</a></li>
<li><a class="reference internal" href="#making-isinstance-obj-list-str-perform-a-check-ignoring-generics">Making <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">list[str])</span></code> perform a check ignoring generics</a></li>
<li><a class="reference internal" href="#making-isinstance-obj-list-str-perform-a-runtime-type-check">Making <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">list[str])</span></code> perform a runtime type check</a></li>
<li><a class="reference internal" href="#naming-the-type-generictype-instead-of-genericalias">Naming the type <code class="docutils literal notranslate"><span class="pre">GenericType</span></code> instead of <code class="docutils literal notranslate"><span class="pre">GenericAlias</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#note-on-the-initial-draft">Note on the initial draft</a></li>
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<div class="pep-banner canonical-doc sticky-banner admonition important">
<p class="admonition-title">Important</p>
<p>This PEP is a historical document. The up-to-date, canonical documentation can now be found at <a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#types-genericalias" title="(in Python v3.13)"><span>Generic Alias Type</span></a>
and the documentation for <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__class_getitem__()</span></code></a>.</p>
<p class="close-button">×</p>
<p>See <a class="pep reference internal" href="../pep-0001/" title="PEP 1 PEP Purpose and Guidelines">PEP 1</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>Static typing as defined by PEPs 484, 526, 544, 560, and 563 was built
incrementally on top of the existing Python runtime and constrained by
existing syntax and runtime behavior. This led to the existence of
a duplicated collection hierarchy in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module due to
generics (for example <code class="docutils literal notranslate"><span class="pre">typing.List</span></code> and the built-in <code class="docutils literal notranslate"><span class="pre">list</span></code>).</p>
<p>This PEP proposes to enable support for the generics syntax in all
standard collections currently available in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module.</p>
</section>
<section id="rationale-and-goals">
<h2><a class="toc-backref" href="#rationale-and-goals" role="doc-backlink">Rationale and Goals</a></h2>
<p>This change removes the necessity for a parallel type hierarchy in the
<code class="docutils literal notranslate"><span class="pre">typing</span></code> module, making it easier for users to annotate their programs
and easier for teachers to teach Python.</p>
</section>
<section id="terminology">
<h2><a class="toc-backref" href="#terminology" role="doc-backlink">Terminology</a></h2>
<p>Generic (n.) a type that can be parameterized, typically a container.
Also known as a <em>parametric type</em> or a <em>generic type</em>. For example:
<code class="docutils literal notranslate"><span class="pre">dict</span></code>.</p>
<p>parameterized generic a specific instance of a generic with the
expected types for container elements provided. Also known as
a <em>parameterized type</em>. For example: <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">int]</span></code>.</p>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards compatibility</a></h2>
<p>Tooling, including type checkers and linters, will have to be adapted to
recognize standard collections as generics.</p>
<p>On the source level, the newly described functionality requires
Python 3.9. For use cases restricted to type annotations, Python files
with the “annotations” future-import (available since Python 3.7) can
parameterize standard collections, including builtins. To reiterate,
that depends on the external tools understanding that this is valid.</p>
</section>
<section id="implementation">
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
<p>Starting with Python 3.7, when <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">annotations</span></code> is
used, function and variable annotations can parameterize standard
collections directly. Example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
<span class="k">def</span> <span class="nf">find</span><span class="p">(</span><span class="n">haystack</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]])</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Usefulness of this syntax before <a class="pep reference internal" href="../pep-0585/" title="PEP 585 Type Hinting Generics In Standard Collections">PEP 585</a> is limited as external tooling
like Mypy does not recognize standard collections as generic. Moreover,
certain features of typing like type aliases or casting require putting
types outside of annotations, in runtime context. While these are
relatively less common than type annotations, its important to allow
using the same type syntax in all contexts. This is why starting with
Python 3.9, the following collections become generic using
<code class="docutils literal notranslate"><span class="pre">__class_getitem__()</span></code> to parameterize contained types:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">tuple</span></code> # typing.Tuple</li>
<li><code class="docutils literal notranslate"><span class="pre">list</span></code> # typing.List</li>
<li><code class="docutils literal notranslate"><span class="pre">dict</span></code> # typing.Dict</li>
<li><code class="docutils literal notranslate"><span class="pre">set</span></code> # typing.Set</li>
<li><code class="docutils literal notranslate"><span class="pre">frozenset</span></code> # typing.FrozenSet</li>
<li><code class="docutils literal notranslate"><span class="pre">type</span></code> # typing.Type</li>
<li><code class="docutils literal notranslate"><span class="pre">collections.deque</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.defaultdict</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.OrderedDict</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.Counter</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.ChainMap</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Awaitable</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Coroutine</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.AsyncIterable</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.AsyncIterator</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.AsyncGenerator</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Iterable</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Iterator</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Generator</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Reversible</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Container</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Collection</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Callable</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Set</span></code> # typing.AbstractSet</li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.MutableSet</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Mapping</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.MutableMapping</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.Sequence</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.MutableSequence</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.ByteString</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.MappingView</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.KeysView</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.ItemsView</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">collections.abc.ValuesView</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">contextlib.AbstractContextManager</span></code> # typing.ContextManager</li>
<li><code class="docutils literal notranslate"><span class="pre">contextlib.AbstractAsyncContextManager</span></code> # typing.AsyncContextManager</li>
<li><code class="docutils literal notranslate"><span class="pre">re.Pattern</span></code> # typing.Pattern, typing.re.Pattern</li>
<li><code class="docutils literal notranslate"><span class="pre">re.Match</span></code> # typing.Match, typing.re.Match</li>
</ul>
<p>Importing those from <code class="docutils literal notranslate"><span class="pre">typing</span></code> is deprecated. Due to <a class="pep reference internal" href="../pep-0563/" title="PEP 563 Postponed Evaluation of Annotations">PEP 563</a> and the
intention to minimize the runtime impact of typing, this deprecation
will not generate DeprecationWarnings. Instead, type checkers may warn
about such deprecated usage when the target version of the checked
program is signalled to be Python 3.9 or newer. Its recommended to
allow for those warnings to be silenced on a project-wide basis.</p>
<p>The deprecated functionality may eventually be removed from the <code class="docutils literal notranslate"><span class="pre">typing</span></code>
module. Removal will occur no sooner than Python 3.9s end of life,
scheduled for October 2025.</p>
<section id="parameters-to-generics-are-available-at-runtime">
<h3><a class="toc-backref" href="#parameters-to-generics-are-available-at-runtime" role="doc-backlink">Parameters to generics are available at runtime</a></h3>
<p>Preserving the generic type at runtime enables introspection of the type
which can be used for API generation or runtime type checking. Such
usage is already present in the wild.</p>
<p>Just like with the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module today, the parameterized generic
types listed in the previous section all preserve their type parameters
at runtime:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="go">list[str]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>
<span class="go">tuple[int, ...]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">ChainMap</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span>
<span class="go">collections.ChainMap[str, list[str]]</span>
</pre></div>
</div>
<p>This is implemented using a thin proxy type that forwards all method
calls and attribute accesses to the bare origin type with the following
exceptions:</p>
<ul class="simple">
<li>the <code class="docutils literal notranslate"><span class="pre">__repr__</span></code> shows the parameterized type;</li>
<li>the <code class="docutils literal notranslate"><span class="pre">__origin__</span></code> attribute points at the non-parameterized
generic class;</li>
<li>the <code class="docutils literal notranslate"><span class="pre">__args__</span></code> attribute is a tuple (possibly of length
1) of generic types passed to the original <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code>;</li>
<li>the <code class="docutils literal notranslate"><span class="pre">__parameters__</span></code> attribute is a lazily computed tuple
(possibly empty) of unique type variables found in <code class="docutils literal notranslate"><span class="pre">__args__</span></code>;</li>
<li>the <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> raises an exception to disallow mistakes
like <code class="docutils literal notranslate"><span class="pre">dict[str][str]</span></code>. However it allows e.g. <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">T][int]</span></code>
and in that case returns <code class="docutils literal notranslate"><span class="pre">dict[str,</span> <span class="pre">int]</span></code>.</li>
</ul>
<p>This design means that it is possible to create instances of
parameterized collections, like:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">l</span> <span class="o">=</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]()</span>
<span class="go">[]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span> <span class="ow">is</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="go">False</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span> <span class="o">==</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="go">False</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">==</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">==</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
<span class="go">False</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">isinstance</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="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span>
<span class="go">TypeError: isinstance() arg 2 cannot be a parameterized generic</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">issubclass</span><span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span>
<span class="go">TypeError: issubclass() arg 2 cannot be a parameterized generic</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">isinstance</span><span class="p">(</span><span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span> <span class="n">types</span><span class="o">.</span><span class="n">GenericAlias</span><span class="p">)</span>
<span class="go">True</span>
</pre></div>
</div>
<p>Objects created with bare types and parameterized types are exactly the
same. The generic parameters are not preserved in instances created
with parameterized types, in other words generic types erase type
parameters during object creation.</p>
<p>One important consequence of this is that the interpreter does <strong>not</strong>
attempt to type check operations on the collection created with
a parameterized type. This provides symmetry between:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">l</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
</pre></div>
</div>
<p>and:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">l</span> <span class="o">=</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]()</span>
</pre></div>
</div>
<p>For accessing the proxy type from Python code, it will be exported
from the <code class="docutils literal notranslate"><span class="pre">types</span></code> module as <code class="docutils literal notranslate"><span class="pre">GenericAlias</span></code>.</p>
<p>Pickling or (shallow- or deep-) copying a <code class="docutils literal notranslate"><span class="pre">GenericAlias</span></code> instance
will preserve the type, origin, attributes and parameters.</p>
</section>
<section id="forward-compatibility">
<h3><a class="toc-backref" href="#forward-compatibility" role="doc-backlink">Forward compatibility</a></h3>
<p>Future standard collections must implement the same behavior.</p>
</section>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference implementation</a></h2>
<p>A proof-of-concept or prototype <a class="reference external" href="https://bugs.python.org/issue39481">implementation</a> exists.</p>
</section>
<section id="rejected-alternatives">
<h2><a class="toc-backref" href="#rejected-alternatives" role="doc-backlink">Rejected alternatives</a></h2>
<section id="do-nothing">
<h3><a class="toc-backref" href="#do-nothing" role="doc-backlink">Do nothing</a></h3>
<p>Keeping the status quo forces Python programmers to perform book-keeping
of imports from the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module for standard collections, making
all but the simplest annotations cumbersome to maintain. The existence
of parallel types is confusing to newcomers (why is there both <code class="docutils literal notranslate"><span class="pre">list</span></code>
and <code class="docutils literal notranslate"><span class="pre">List</span></code>?).</p>
<p>The above problems also dont exist in user-built generic classes which
share runtime functionality and the ability to use them as generic type
annotations. Making standard collections harder to use in type hinting
from user classes hindered typing adoption and usability.</p>
</section>
<section id="generics-erasure">
<h3><a class="toc-backref" href="#generics-erasure" role="doc-backlink">Generics erasure</a></h3>
<p>It would be easier to implement <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> on the listed
standard collections in a way that doesnt preserve the generic type,
in other words:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="go">&lt;class &#39;list&#39;&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>
<span class="go">&lt;class &#39;tuple&#39;&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">collections</span><span class="o">.</span><span class="n">ChainMap</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span>
<span class="go">&lt;class &#39;collections.ChainMap&#39;&gt;</span>
</pre></div>
</div>
<p>This is problematic as it breaks backwards compatibility: current
equivalents of those types in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module <strong>do</strong> preserve
the generic type:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">ChainMap</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span>
<span class="go">typing.List[str]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>
<span class="go">typing.Tuple[int, ...]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">ChainMap</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">str</span><span class="p">]]</span>
<span class="go">typing.ChainMap[str, typing.List[str]]</span>
</pre></div>
</div>
<p>As mentioned in the “Implementation” section, preserving the generic
type at runtime enables runtime introspection of the type which can be
used for API generation or runtime type checking. Such usage is already
present in the wild.</p>
<p>Additionally, implementing subscripts as identity functions would make
Python less friendly to beginners. Say, if a user is mistakenly passing
a list type instead of a list object to a function, and that function is
indexing the received object, the code would no longer raise an error.</p>
<p>Today:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">l</span> <span class="o">=</span> <span class="nb">list</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">l</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="go">TypeError: &#39;type&#39; object is not subscriptable</span>
</pre></div>
</div>
<p>With <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> as an identity function:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">l</span> <span class="o">=</span> <span class="nb">list</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">l</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="go">list</span>
</pre></div>
</div>
<p>The indexing being successful here would likely end up raising an
exception at a distance, confusing the user.</p>
</section>
<section id="disallowing-instantiation-of-parameterized-types">
<h3><a class="toc-backref" href="#disallowing-instantiation-of-parameterized-types" role="doc-backlink">Disallowing instantiation of parameterized types</a></h3>
<p>Given that the proxy type which preserves <code class="docutils literal notranslate"><span class="pre">__origin__</span></code> and
<code class="docutils literal notranslate"><span class="pre">__args__</span></code> is mostly useful for runtime introspection purposes,
we might have disallowed instantiation of parameterized types.</p>
<p>In fact, forbidding instantiation of parameterized types is what the
<code class="docutils literal notranslate"><span class="pre">typing</span></code> module does today for types which parallel builtin
collections (instantiation of other parameterized types is allowed).</p>
<p>The original reason for this decision was to discourage spurious
parameterization which made object creation up to two orders of magnitude
slower compared to the special syntax available for those builtin
collections.</p>
<p>This rationale is not strong enough to allow the exceptional treatment
of builtins. All other parameterized types can be instantiated,
including parallels of collections in the standard library. Moreover,
Python allows for instantiation of lists using <code class="docutils literal notranslate"><span class="pre">list()</span></code> and some
builtin collections dont provide special syntax for instantiation.</p>
</section>
<section id="making-isinstance-obj-list-str-perform-a-check-ignoring-generics">
<h3><a class="toc-backref" href="#making-isinstance-obj-list-str-perform-a-check-ignoring-generics" role="doc-backlink">Making <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">list[str])</span></code> perform a check ignoring generics</a></h3>
<p>An earlier version of this PEP suggested treating parameterized generics
like <code class="docutils literal notranslate"><span class="pre">list[str]</span></code> as equivalent to their non-parameterized variants
like <code class="docutils literal notranslate"><span class="pre">list</span></code> for purposes of <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass()</span></code>.
This would be symmetrical to how <code class="docutils literal notranslate"><span class="pre">list[str]()</span></code> creates a regular list.</p>
<p>This design was rejected because <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass()</span></code>
checks with parameterized generics would read like element-by-element
runtime type checks. The result of those checks would be surprising,
for example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">isinstance</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="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span>
<span class="go">True</span>
</pre></div>
</div>
<p>Note the object doesnt match the provided generic type but
<code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> still returns <code class="docutils literal notranslate"><span class="pre">True</span></code> because it only checks whether
the object is a list.</p>
<p>If a library is faced with a parameterized generic and would like to
perform an <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> check using the base type, that type can
be retrieved using the <code class="docutils literal notranslate"><span class="pre">__origin__</span></code> attribute on the parameterized
generic.</p>
</section>
<section id="making-isinstance-obj-list-str-perform-a-runtime-type-check">
<h3><a class="toc-backref" href="#making-isinstance-obj-list-str-perform-a-runtime-type-check" role="doc-backlink">Making <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">list[str])</span></code> perform a runtime type check</a></h3>
<p>This functionality requires iterating over the collection which is
a destructive operation in some of them. This functionality would have
been useful, however implementing the type checker within Python that
would deal with complex types, nested type checking, type variables,
string forward references, and so on is out of scope for this PEP.</p>
</section>
<section id="naming-the-type-generictype-instead-of-genericalias">
<h3><a class="toc-backref" href="#naming-the-type-generictype-instead-of-genericalias" role="doc-backlink">Naming the type <code class="docutils literal notranslate"><span class="pre">GenericType</span></code> instead of <code class="docutils literal notranslate"><span class="pre">GenericAlias</span></code></a></h3>
<p>We considered a different name for this type, but decided
<code class="docutils literal notranslate"><span class="pre">GenericAlias</span></code> is better these arent real types, they are
aliases for the corresponding container type with some extra metadata
attached.</p>
</section>
</section>
<section id="note-on-the-initial-draft">
<h2><a class="toc-backref" href="#note-on-the-initial-draft" role="doc-backlink">Note on the initial draft</a></h2>
<p>An early version of this PEP discussed matters beyond generics in
standard collections. Those unrelated topics were removed for clarity.</p>
</section>
<section id="acknowledgments">
<h2><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h2>
<p>Thank you to Guido van Rossum for his work on Python, and the
implementation of this PEP specifically.</p>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0585.rst">https://github.com/python/peps/blob/main/peps/pep-0585.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0585.rst">2024-06-11 22:12:09 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="#rationale-and-goals">Rationale and Goals</a></li>
<li><a class="reference internal" href="#terminology">Terminology</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards compatibility</a></li>
<li><a class="reference internal" href="#implementation">Implementation</a><ul>
<li><a class="reference internal" href="#parameters-to-generics-are-available-at-runtime">Parameters to generics are available at runtime</a></li>
<li><a class="reference internal" href="#forward-compatibility">Forward compatibility</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference implementation</a></li>
<li><a class="reference internal" href="#rejected-alternatives">Rejected alternatives</a><ul>
<li><a class="reference internal" href="#do-nothing">Do nothing</a></li>
<li><a class="reference internal" href="#generics-erasure">Generics erasure</a></li>
<li><a class="reference internal" href="#disallowing-instantiation-of-parameterized-types">Disallowing instantiation of parameterized types</a></li>
<li><a class="reference internal" href="#making-isinstance-obj-list-str-perform-a-check-ignoring-generics">Making <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">list[str])</span></code> perform a check ignoring generics</a></li>
<li><a class="reference internal" href="#making-isinstance-obj-list-str-perform-a-runtime-type-check">Making <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">list[str])</span></code> perform a runtime type check</a></li>
<li><a class="reference internal" href="#naming-the-type-generictype-instead-of-genericalias">Naming the type <code class="docutils literal notranslate"><span class="pre">GenericType</span></code> instead of <code class="docutils literal notranslate"><span class="pre">GenericAlias</span></code></a></li>
</ul>
</li>
<li><a class="reference internal" href="#note-on-the-initial-draft">Note on the initial draft</a></li>
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</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-0585.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>