CINXE.COM
PEP 585 – Type Hinting Generics In Standard Collections | peps.python.org
<!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> » </li> <li><a href="../pep-0000/">PEP Index</a> » </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 <lukasz at python.org></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@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@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">-></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, it’s 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. It’s 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.9’s 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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </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 don’t 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 doesn’t preserve the generic type, in other words:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="go"><class 'list'></span> <span class="gp">>>> </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"><class 'tuple'></span> <span class="gp">>>> </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"><class 'collections.ChainMap'></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">>>> </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">>>> </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">>>> </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">>>> </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">>>> </span><span class="n">l</span> <span class="o">=</span> <span class="nb">list</span> <span class="gp">>>> </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: 'type' 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">>>> </span><span class="n">l</span> <span class="o">=</span> <span class="nb">list</span> <span class="gp">>>> </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 don’t 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">>>> </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 doesn’t 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 aren’t 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>