peps/pep-0560/index.html

454 lines
40 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 560 Core support for typing module and generic types | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0560/">
<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 560 Core support for typing module and generic types | peps.python.org'>
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0560/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="Python Enhancement Proposals (PEPs)">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 560</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 560 Core support for typing module and generic types</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Ivan Levkivskyi &lt;levkivskyi&#32;&#97;t&#32;gmail.com&gt;</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">Created<span class="colon">:</span></dt>
<dd class="field-even">03-Sep-2017</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.7</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even">09-Sep-2017, 14-Nov-2017</dd>
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-December/151038.html">Python-Dev message</a></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#performance">Performance</a></li>
<li><a class="reference internal" href="#metaclass-conflicts">Metaclass conflicts</a></li>
<li><a class="reference internal" href="#hacks-and-bugs-that-will-be-removed-by-this-proposal">Hacks and bugs that will be removed by this proposal</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#class-getitem"><code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code></a></li>
<li><a class="reference internal" href="#mro-entries"><code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code></a></li>
<li><a class="reference internal" href="#dynamic-class-creation-and-types-resolve-bases">Dynamic class creation and <code class="docutils literal notranslate"><span class="pre">types.resolve_bases</span></code></a></li>
<li><a class="reference internal" href="#using-class-getitem-in-c-extensions">Using <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> in C extensions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility-and-impact-on-users-who-don-t-use-typing">Backwards compatibility and impact on users who dont use <code class="docutils literal notranslate"><span class="pre">typing</span></code></a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<div class="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/reference/datamodel.html#object.__class_getitem__" title="(in Python v3.12)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__class_getitem__()</span></code></a> and
<a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__mro_entries__" title="(in Python v3.12)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__mro_entries__()</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>Initially <a class="pep reference internal" href="../pep-0484/" title="PEP 484 Type Hints">PEP 484</a> was designed in such way that it would not introduce
<em>any</em> changes to the core CPython interpreter. Now type hints and
the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module are extensively used by the community, e.g. <a class="pep reference internal" href="../pep-0526/" title="PEP 526 Syntax for Variable Annotations">PEP 526</a>
and <a class="pep reference internal" href="../pep-0557/" title="PEP 557 Data Classes">PEP 557</a> extend the usage of type hints, and the backport of <code class="docutils literal notranslate"><span class="pre">typing</span></code>
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
It is proposed to add two special methods <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> and
<code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code> to the core CPython for better support of
generic types.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>The restriction to not modify the core CPython interpreter led to some
design decisions that became questionable when the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module started
to be widely used. There are three main points of concern:
performance of the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module, metaclass conflicts, and the large
number of hacks currently used in <code class="docutils literal notranslate"><span class="pre">typing</span></code>.</p>
<section id="performance">
<h3><a class="toc-backref" href="#performance" role="doc-backlink">Performance</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">typing</span></code> module is one of the heaviest and slowest modules in
the standard library even with all the optimizations made. Mainly this is
because subscripted generic types (see <a class="pep reference internal" href="../pep-0484/" title="PEP 484 Type Hints">PEP 484</a> for definition of terms used
in this PEP) are class objects (see also <a class="footnote-reference brackets" href="#id7" id="id1">[1]</a>). There are three main ways how
the performance can be improved with the help of the proposed special methods:</p>
<ul class="simple">
<li>Creation of generic classes is slow since the <code class="docutils literal notranslate"><span class="pre">GenericMeta.__new__</span></code> is
very slow; we will not need it anymore.</li>
<li>Very long method resolution orders (MROs) for generic classes will be
half as long; they are present because we duplicate the <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code>
inheritance chain in <code class="docutils literal notranslate"><span class="pre">typing</span></code>.</li>
<li>Instantiation of generic classes will be faster (this is minor however).</li>
</ul>
</section>
<section id="metaclass-conflicts">
<h3><a class="toc-backref" href="#metaclass-conflicts" role="doc-backlink">Metaclass conflicts</a></h3>
<p>All generic types are instances of <code class="docutils literal notranslate"><span class="pre">GenericMeta</span></code>, so if a user uses
a custom metaclass, then it is hard to make a corresponding class generic.
This is particularly hard for library classes that a user doesnt control.
A workaround is to always mix-in <code class="docutils literal notranslate"><span class="pre">GenericMeta</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">AdHocMeta</span><span class="p">(</span><span class="n">GenericMeta</span><span class="p">,</span> <span class="n">LibraryMeta</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">UserClass</span><span class="p">(</span><span class="n">LibraryBase</span><span class="p">,</span> <span class="n">Generic</span><span class="p">[</span><span class="n">T</span><span class="p">],</span> <span class="n">metaclass</span><span class="o">=</span><span class="n">AdHocMeta</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</div>
<p>but this is not always practical or even possible. With the help of the
proposed special attributes the <code class="docutils literal notranslate"><span class="pre">GenericMeta</span></code> metaclass will not be needed.</p>
</section>
<section id="hacks-and-bugs-that-will-be-removed-by-this-proposal">
<h3><a class="toc-backref" href="#hacks-and-bugs-that-will-be-removed-by-this-proposal" role="doc-backlink">Hacks and bugs that will be removed by this proposal</a></h3>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">_generic_new</span></code> hack that exists because <code class="docutils literal notranslate"><span class="pre">__init__</span></code> is not called on
instances with a type differing from the type whose <code class="docutils literal notranslate"><span class="pre">__new__</span></code> was called,
<code class="docutils literal notranslate"><span class="pre">C[int]().__class__</span> <span class="pre">is</span> <span class="pre">C</span></code>.</li>
<li><code class="docutils literal notranslate"><span class="pre">_next_in_mro</span></code> speed hack will be not necessary since subscription will
not create new classes.</li>
<li>Ugly <code class="docutils literal notranslate"><span class="pre">sys._getframe</span></code> hack. This one is particularly nasty since it looks
like we cant remove it without changes outside <code class="docutils literal notranslate"><span class="pre">typing</span></code>.</li>
<li>Currently generics do dangerous things with private ABC caches
to fix large memory consumption that grows at least as O(N<sup>2</sup>),
see <a class="footnote-reference brackets" href="#id8" id="id2">[2]</a>. This point is also important because it was recently proposed to
re-implement <code class="docutils literal notranslate"><span class="pre">ABCMeta</span></code> in C.</li>
<li>Problems with sharing attributes between subscripted generics,
see <a class="footnote-reference brackets" href="#id9" id="id3">[3]</a>. The current solution already uses <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> and <code class="docutils literal notranslate"><span class="pre">__setattr__</span></code>,
but it is still incomplete, and solving this without the current proposal
will be hard and will need <code class="docutils literal notranslate"><span class="pre">__getattribute__</span></code>.</li>
<li><code class="docutils literal notranslate"><span class="pre">_no_slots_copy</span></code> hack, where we clean up the class dictionary on every
subscription thus allowing generics with <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>.</li>
<li>General complexity of the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module. The new proposal will not
only allow to remove the above-mentioned hacks/bugs, but also simplify
the implementation, so that it will be easier to maintain.</li>
</ul>
</section>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<section id="class-getitem">
<h3><a class="toc-backref" href="#class-getitem" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code></a></h3>
<p>The idea of <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> is simple: it is an exact analog of
<code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> with an exception that it is called on a class that
defines it, not on its instances. This allows us to avoid
<code class="docutils literal notranslate"><span class="pre">GenericMeta.__getitem__</span></code> for things like <code class="docutils literal notranslate"><span class="pre">Iterable[int]</span></code>.
The <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> is automatically a class method and
does not require <code class="docutils literal notranslate"><span class="pre">&#64;classmethod</span></code> decorator (similar to
<code class="docutils literal notranslate"><span class="pre">__init_subclass__</span></code>) and is inherited like normal attributes.
For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">MyList</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">):</span>
<span class="k">return</span> <span class="n">index</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">__class_getitem__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">[</span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">]&quot;</span>
<span class="k">class</span> <span class="nc">MyOtherList</span><span class="p">(</span><span class="n">MyList</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">assert</span> <span class="n">MyList</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="k">assert</span> <span class="n">MyList</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;MyList[int]&quot;</span>
<span class="k">assert</span> <span class="n">MyOtherList</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="k">assert</span> <span class="n">MyOtherList</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;MyOtherList[int]&quot;</span>
</pre></div>
</div>
<p>Note that this method is used as a fallback, so if a metaclass defines
<code class="docutils literal notranslate"><span class="pre">__getitem__</span></code>, then that will have the priority.</p>
</section>
<section id="mro-entries">
<h3><a class="toc-backref" href="#mro-entries" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code></a></h3>
<p>If an object that is not a class object appears in the tuple of bases of
a class definition, then method <code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code> is searched on it.
If found, it is called with the original tuple of bases as an argument.
The result of the call must be a tuple, that is unpacked in the base classes
in place of this object. (If the tuple is empty, this means that the original
bases is simply discarded.) If there are more than one object with
<code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code>, then all of them are called with the same original tuple
of bases. This step happens first in the process of creation of a class,
all other steps, including checks for duplicate bases and MRO calculation,
happen normally with the updated bases.</p>
<p>Using the method API instead of just an attribute is necessary to avoid
inconsistent MRO errors, and perform other manipulations that are currently
done by <code class="docutils literal notranslate"><span class="pre">GenericMeta.__new__</span></code>. The original bases are stored as
<code class="docutils literal notranslate"><span class="pre">__orig_bases__</span></code> in the class namespace (currently this is also done by
the metaclass). For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">GenericAlias</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">origin</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">origin</span> <span class="o">=</span> <span class="n">origin</span>
<span class="bp">self</span><span class="o">.</span><span class="n">item</span> <span class="o">=</span> <span class="n">item</span>
<span class="k">def</span> <span class="nf">__mro_entries__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">bases</span><span class="p">):</span>
<span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">origin</span><span class="p">,)</span>
<span class="k">class</span> <span class="nc">NewList</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__class_getitem__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
<span class="k">return</span> <span class="n">GenericAlias</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">item</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">Tokens</span><span class="p">(</span><span class="n">NewList</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span>
<span class="o">...</span>
<span class="k">assert</span> <span class="n">Tokens</span><span class="o">.</span><span class="vm">__bases__</span> <span class="o">==</span> <span class="p">(</span><span class="n">NewList</span><span class="p">,)</span>
<span class="k">assert</span> <span class="n">Tokens</span><span class="o">.</span><span class="n">__orig_bases__</span> <span class="o">==</span> <span class="p">(</span><span class="n">NewList</span><span class="p">[</span><span class="nb">int</span><span class="p">],)</span>
<span class="k">assert</span> <span class="n">Tokens</span><span class="o">.</span><span class="vm">__mro__</span> <span class="o">==</span> <span class="p">(</span><span class="n">Tokens</span><span class="p">,</span> <span class="n">NewList</span><span class="p">,</span> <span class="nb">object</span><span class="p">)</span>
</pre></div>
</div>
<p>Resolution using <code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code> happens <em>only</em> in bases of a class
definition statement. In all other situations where a class object is
expected, no such resolution will happen, this includes <code class="docutils literal notranslate"><span class="pre">isinstance</span></code>
and <code class="docutils literal notranslate"><span class="pre">issubclass</span></code> built-in functions.</p>
<p>NOTE: These two method names are reserved for use by the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module
and the generic types machinery, and any other use is discouraged.
The reference implementation (with tests) can be found in <a class="footnote-reference brackets" href="#id10" id="id4">[4]</a>, and
the proposal was originally posted and discussed on the <code class="docutils literal notranslate"><span class="pre">typing</span></code> tracker,
see <a class="footnote-reference brackets" href="#id11" id="id5">[5]</a>.</p>
</section>
<section id="dynamic-class-creation-and-types-resolve-bases">
<h3><a class="toc-backref" href="#dynamic-class-creation-and-types-resolve-bases" role="doc-backlink">Dynamic class creation and <code class="docutils literal notranslate"><span class="pre">types.resolve_bases</span></code></a></h3>
<p><code class="docutils literal notranslate"><span class="pre">type.__new__</span></code> will not perform any MRO entry resolution. So that a direct
call <code class="docutils literal notranslate"><span class="pre">type('Tokens',</span> <span class="pre">(List[int],),</span> <span class="pre">{})</span></code> will fail. This is done for
performance reasons and to minimize the number of implicit transformations.
Instead, a helper function <code class="docutils literal notranslate"><span class="pre">resolve_bases</span></code> will be added to
the <code class="docutils literal notranslate"><span class="pre">types</span></code> module to allow an explicit <code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code> resolution in
the context of dynamic class creation. Correspondingly, <code class="docutils literal notranslate"><span class="pre">types.new_class</span></code>
will be updated to reflect the new class creation steps while maintaining
the backwards compatibility:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">new_class</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="o">=</span><span class="p">(),</span> <span class="n">kwds</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">exec_body</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">resolved_bases</span> <span class="o">=</span> <span class="n">resolve_bases</span><span class="p">(</span><span class="n">bases</span><span class="p">)</span> <span class="c1"># This step is added</span>
<span class="n">meta</span><span class="p">,</span> <span class="n">ns</span><span class="p">,</span> <span class="n">kwds</span> <span class="o">=</span> <span class="n">prepare_class</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">resolved_bases</span><span class="p">,</span> <span class="n">kwds</span><span class="p">)</span>
<span class="k">if</span> <span class="n">exec_body</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">exec_body</span><span class="p">(</span><span class="n">ns</span><span class="p">)</span>
<span class="n">ns</span><span class="p">[</span><span class="s1">&#39;__orig_bases__&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">bases</span> <span class="c1"># This step is added</span>
<span class="k">return</span> <span class="n">meta</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">resolved_bases</span><span class="p">,</span> <span class="n">ns</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="using-class-getitem-in-c-extensions">
<h3><a class="toc-backref" href="#using-class-getitem-in-c-extensions" role="doc-backlink">Using <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> in C extensions</a></h3>
<p>As mentioned above, <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> is automatically a class method
if defined in Python code. To define this method in a C extension, one
should use flags <code class="docutils literal notranslate"><span class="pre">METH_O|METH_CLASS</span></code>. For example, a simple way to make
an extension class generic is to use a method that simply returns the
original class objects, thus fully erasing the type information at runtime,
and deferring all check to static type checkers only:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span>
<span class="n">PyObject_HEAD</span>
<span class="o">/*</span> <span class="o">...</span> <span class="n">your</span> <span class="n">code</span> <span class="o">...</span> <span class="o">*/</span>
<span class="p">}</span> <span class="n">SimpleGeneric</span><span class="p">;</span>
<span class="n">static</span> <span class="n">PyObject</span> <span class="o">*</span>
<span class="n">simple_class_getitem</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="nb">type</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">item</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Py_INCREF</span><span class="p">(</span><span class="nb">type</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">type</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">static</span> <span class="n">PyMethodDef</span> <span class="n">simple_generic_methods</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">{</span><span class="s2">&quot;__class_getitem__&quot;</span><span class="p">,</span> <span class="n">simple_class_getitem</span><span class="p">,</span> <span class="n">METH_O</span><span class="o">|</span><span class="n">METH_CLASS</span><span class="p">,</span> <span class="n">NULL</span><span class="p">},</span>
<span class="o">/*</span> <span class="o">...</span> <span class="n">other</span> <span class="n">methods</span> <span class="o">...</span> <span class="o">*/</span>
<span class="p">};</span>
<span class="n">PyTypeObject</span> <span class="n">SimpleGeneric_Type</span> <span class="o">=</span> <span class="p">{</span>
<span class="n">PyVarObject_HEAD_INIT</span><span class="p">(</span><span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="s2">&quot;SimpleGeneric&quot;</span><span class="p">,</span>
<span class="n">sizeof</span><span class="p">(</span><span class="n">SimpleGeneric</span><span class="p">),</span>
<span class="mi">0</span><span class="p">,</span>
<span class="o">.</span><span class="n">tp_flags</span> <span class="o">=</span> <span class="n">Py_TPFLAGS_DEFAULT</span> <span class="o">|</span> <span class="n">Py_TPFLAGS_BASETYPE</span><span class="p">,</span>
<span class="o">.</span><span class="n">tp_methods</span> <span class="o">=</span> <span class="n">simple_generic_methods</span><span class="p">,</span>
<span class="p">};</span>
</pre></div>
</div>
<p>Such class can be used as a normal generic in Python type annotations
(a corresponding stub file should be provided for static type checkers,
see <a class="pep reference internal" href="../pep-0484/" title="PEP 484 Type Hints">PEP 484</a> for details):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">simple_extension</span> <span class="kn">import</span> <span class="n">SimpleGeneric</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypeVar</span>
<span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">&#39;T&#39;</span><span class="p">)</span>
<span class="n">Alias</span> <span class="o">=</span> <span class="n">SimpleGeneric</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">T</span><span class="p">]</span>
<span class="k">class</span> <span class="nc">SubClass</span><span class="p">(</span><span class="n">SimpleGeneric</span><span class="p">[</span><span class="n">T</span><span class="p">,</span> <span class="nb">int</span><span class="p">]):</span>
<span class="o">...</span>
<span class="n">data</span><span class="p">:</span> <span class="n">Alias</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="c1"># Works at runtime</span>
<span class="n">more_data</span><span class="p">:</span> <span class="n">SubClass</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="c1"># Also works at runtime</span>
</pre></div>
</div>
</section>
</section>
<section id="backwards-compatibility-and-impact-on-users-who-don-t-use-typing">
<h2><a class="toc-backref" href="#backwards-compatibility-and-impact-on-users-who-don-t-use-typing" role="doc-backlink">Backwards compatibility and impact on users who dont use <code class="docutils literal notranslate"><span class="pre">typing</span></code></a></h2>
<p>This proposal may break code that currently uses the names
<code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> and <code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code>. (But the language
reference explicitly reserves <em>all</em> undocumented dunder names, and
allows “breakage without warning”; see <a class="footnote-reference brackets" href="#id12" id="id6">[6]</a>.)</p>
<p>This proposal will support almost complete backwards compatibility with
the current public generic types API; moreover the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module is still
provisional. The only two exceptions are that currently
<code class="docutils literal notranslate"><span class="pre">issubclass(List[int],</span> <span class="pre">List)</span></code> returns True, while with this proposal it will
raise <code class="docutils literal notranslate"><span class="pre">TypeError</span></code>, and <code class="docutils literal notranslate"><span class="pre">repr()</span></code> of unsubscripted user-defined generics
cannot be tweaked and will coincide with <code class="docutils literal notranslate"><span class="pre">repr()</span></code> of normal (non-generic)
classes.</p>
<p>With the reference implementation I measured negligible performance effects
(under 1% on a micro-benchmark) for regular (non-generic) classes. At the same
time performance of generics is significantly improved:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">importlib.reload(typing)</span></code> is up to 7x faster</li>
<li>Creation of user defined generic classes is up to 4x faster (on a
micro-benchmark with an empty body)</li>
<li>Instantiation of generic classes is up to 5x faster (on a micro-benchmark
with an empty <code class="docutils literal notranslate"><span class="pre">__init__</span></code>)</li>
<li>Other operations with generic types and instances (like method lookup and
<code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> checks) are improved by around 10-20%</li>
<li>The only aspect that gets slower with the current proof of concept
implementation is the subscripted generics cache look-up. However it was
already very efficient, so this aspect gives negligible overall impact.</li>
</ul>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id7" role="doc-footnote">
<dt class="label" id="id7">[<a href="#id1">1</a>]</dt>
<dd>Discussion following Mark Shannons presentation at Language Summit
(<a class="reference external" href="https://github.com/python/typing/issues/432">https://github.com/python/typing/issues/432</a>)</aside>
<aside class="footnote brackets" id="id8" role="doc-footnote">
<dt class="label" id="id8">[<a href="#id2">2</a>]</dt>
<dd>Pull Request to implement shared generic ABC caches (merged)
(<a class="reference external" href="https://github.com/python/typing/pull/383">https://github.com/python/typing/pull/383</a>)</aside>
<aside class="footnote brackets" id="id9" role="doc-footnote">
<dt class="label" id="id9">[<a href="#id3">3</a>]</dt>
<dd>An old bug with setting/accessing attributes on generic types
(<a class="reference external" href="https://github.com/python/typing/issues/392">https://github.com/python/typing/issues/392</a>)</aside>
<aside class="footnote brackets" id="id10" role="doc-footnote">
<dt class="label" id="id10">[<a href="#id4">4</a>]</dt>
<dd>The reference implementation
(<a class="reference external" href="https://github.com/ilevkivskyi/cpython/pull/2/files">https://github.com/ilevkivskyi/cpython/pull/2/files</a>,
<a class="reference external" href="https://github.com/ilevkivskyi/cpython/tree/new-typing">https://github.com/ilevkivskyi/cpython/tree/new-typing</a>)</aside>
<aside class="footnote brackets" id="id11" role="doc-footnote">
<dt class="label" id="id11">[<a href="#id5">5</a>]</dt>
<dd>Original proposal
(<a class="reference external" href="https://github.com/python/typing/issues/468">https://github.com/python/typing/issues/468</a>)</aside>
<aside class="footnote brackets" id="id12" role="doc-footnote">
<dt class="label" id="id12">[<a href="#id6">6</a>]</dt>
<dd>Reserved classes of identifiers
(<a class="reference external" href="https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers">https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers</a>)</aside>
</aside>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed in the public domain.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0560.rst">https://github.com/python/peps/blob/main/peps/pep-0560.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0560.rst">2024-02-07 15:02:53 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">Rationale</a><ul>
<li><a class="reference internal" href="#performance">Performance</a></li>
<li><a class="reference internal" href="#metaclass-conflicts">Metaclass conflicts</a></li>
<li><a class="reference internal" href="#hacks-and-bugs-that-will-be-removed-by-this-proposal">Hacks and bugs that will be removed by this proposal</a></li>
</ul>
</li>
<li><a class="reference internal" href="#specification">Specification</a><ul>
<li><a class="reference internal" href="#class-getitem"><code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code></a></li>
<li><a class="reference internal" href="#mro-entries"><code class="docutils literal notranslate"><span class="pre">__mro_entries__</span></code></a></li>
<li><a class="reference internal" href="#dynamic-class-creation-and-types-resolve-bases">Dynamic class creation and <code class="docutils literal notranslate"><span class="pre">types.resolve_bases</span></code></a></li>
<li><a class="reference internal" href="#using-class-getitem-in-c-extensions">Using <code class="docutils literal notranslate"><span class="pre">__class_getitem__</span></code> in C extensions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backwards-compatibility-and-impact-on-users-who-don-t-use-typing">Backwards compatibility and impact on users who dont use <code class="docutils literal notranslate"><span class="pre">typing</span></code></a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0560.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>