1630 lines
155 KiB
HTML
1630 lines
155 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 544 – Protocols: Structural subtyping (static duck typing) | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0544/">
|
||
<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 544 – Protocols: Structural subtyping (static duck typing) | peps.python.org'>
|
||
<meta property="og:description" content="Type hints introduced in PEP 484 can be used to specify type metadata for static type checkers and other third party tools. However, PEP 484 only specifies the semantics of nominal subtyping. In this PEP we specify static and runtime semantics of protoc...">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0544/">
|
||
<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="Type hints introduced in PEP 484 can be used to specify type metadata for static type checkers and other third party tools. However, PEP 484 only specifies the semantics of nominal subtyping. In this PEP we specify static and runtime semantics of protoc...">
|
||
<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 544</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 544 – Protocols: Structural subtyping (static duck typing)</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Ivan Levkivskyi <levkivskyi at gmail.com>, Jukka Lehtosalo <jukka.lehtosalo at iki.fi>, Łukasz Langa <lukasz at python.org></dd>
|
||
<dt class="field-even">BDFL-Delegate<span class="colon">:</span></dt>
|
||
<dd class="field-even">Guido van Rossum <guido at python.org></dd>
|
||
<dt class="field-odd">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/">Python-Dev list</a></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Topic<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="../topic/typing/">Typing</a></dd>
|
||
<dt class="field-odd">Created<span class="colon">:</span></dt>
|
||
<dd class="field-odd">05-Mar-2017</dd>
|
||
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-even">3.8</dd>
|
||
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/typing-sig@python.org/message/FDO4KFYWYQEP3U2HVVBEBR3SXPHQSHYR/">Typing-SIG message</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#rationale-and-goals">Rationale and Goals</a><ul>
|
||
<li><a class="reference internal" href="#nominal-vs-structural-subtyping">Nominal vs structural subtyping</a></li>
|
||
<li><a class="reference internal" href="#non-goals">Non-goals</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#existing-approaches-to-structural-subtyping">Existing Approaches to Structural Subtyping</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#terminology">Terminology</a></li>
|
||
<li><a class="reference internal" href="#defining-a-protocol">Defining a protocol</a></li>
|
||
<li><a class="reference internal" href="#protocol-members">Protocol members</a></li>
|
||
<li><a class="reference internal" href="#explicitly-declaring-implementation">Explicitly declaring implementation</a></li>
|
||
<li><a class="reference internal" href="#merging-and-extending-protocols">Merging and extending protocols</a></li>
|
||
<li><a class="reference internal" href="#generic-protocols">Generic protocols</a></li>
|
||
<li><a class="reference internal" href="#recursive-protocols">Recursive protocols</a></li>
|
||
<li><a class="reference internal" href="#self-types-in-protocols">Self-types in protocols</a></li>
|
||
<li><a class="reference internal" href="#callback-protocols">Callback protocols</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#using-protocols">Using Protocols</a><ul>
|
||
<li><a class="reference internal" href="#subtyping-relationships-with-other-types">Subtyping relationships with other types</a></li>
|
||
<li><a class="reference internal" href="#unions-and-intersections-of-protocols">Unions and intersections of protocols</a></li>
|
||
<li><a class="reference internal" href="#type-and-class-objects-vs-protocols"><code class="docutils literal notranslate"><span class="pre">Type[]</span></code> and class objects vs protocols</a></li>
|
||
<li><a class="reference internal" href="#newtype-and-type-aliases"><code class="docutils literal notranslate"><span class="pre">NewType()</span></code> and type aliases</a></li>
|
||
<li><a class="reference internal" href="#modules-as-implementations-of-protocols">Modules as implementations of protocols</a></li>
|
||
<li><a class="reference internal" href="#runtime-checkable-decorator-and-narrowing-types-by-isinstance"><code class="docutils literal notranslate"><span class="pre">@runtime_checkable</span></code> decorator and narrowing types by <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#using-protocols-in-python-2-7-3-5">Using Protocols in Python 2.7 - 3.5</a></li>
|
||
<li><a class="reference internal" href="#runtime-implementation-of-protocol-classes">Runtime Implementation of Protocol Classes</a><ul>
|
||
<li><a class="reference internal" href="#implementation-details">Implementation details</a></li>
|
||
<li><a class="reference internal" href="#changes-in-the-typing-module">Changes in the typing module</a></li>
|
||
<li><a class="reference internal" href="#introspection">Introspection</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-postponed-ideas">Rejected/Postponed Ideas</a><ul>
|
||
<li><a class="reference internal" href="#make-every-class-a-protocol-by-default">Make every class a protocol by default</a></li>
|
||
<li><a class="reference internal" href="#protocols-subclassing-normal-classes">Protocols subclassing normal classes</a></li>
|
||
<li><a class="reference internal" href="#support-optional-protocol-members">Support optional protocol members</a></li>
|
||
<li><a class="reference internal" href="#allow-only-protocol-methods-and-force-use-of-getters-and-setters">Allow only protocol methods and force use of getters and setters</a></li>
|
||
<li><a class="reference internal" href="#support-non-protocol-members">Support non-protocol members</a></li>
|
||
<li><a class="reference internal" href="#make-protocols-interoperable-with-other-approaches">Make protocols interoperable with other approaches</a></li>
|
||
<li><a class="reference internal" href="#use-assignments-to-check-explicitly-that-a-class-implements-a-protocol">Use assignments to check explicitly that a class implements a protocol</a></li>
|
||
<li><a class="reference internal" href="#support-isinstance-checks-by-default">Support <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> checks by default</a></li>
|
||
<li><a class="reference internal" href="#provide-a-special-intersection-type-construct">Provide a special intersection type construct</a></li>
|
||
<li><a class="reference internal" href="#prohibit-explicit-subclassing-of-protocols-by-non-protocols">Prohibit explicit subclassing of protocols by non-protocols</a></li>
|
||
<li><a class="reference internal" href="#covariant-subtyping-of-mutable-attributes">Covariant subtyping of mutable attributes</a></li>
|
||
<li><a class="reference internal" href="#overriding-inferred-variance-of-protocol-classes">Overriding inferred variance of protocol classes</a></li>
|
||
<li><a class="reference internal" href="#support-adapters-and-adaptation">Support adapters and adaptation</a></li>
|
||
<li><a class="reference internal" href="#call-structural-base-types-interfaces">Call structural base types “interfaces”</a></li>
|
||
<li><a class="reference internal" href="#make-protocols-special-objects-at-runtime-rather-than-normal-abcs">Make protocols special objects at runtime rather than normal ABCs</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</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-typing-spec sticky-banner admonition attention">
|
||
<p class="admonition-title">Attention</p>
|
||
<p>This PEP is a historical document: see <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/protocol.html#protocols" title="(in typing)"><span>Protocols</span></a> and
|
||
<a class="reference external" href="https://docs.python.org/3/library/typing.html#typing.Protocol" title="(in Python v3.13)"><code class="xref py py-class docutils literal notranslate"><span class="pre">typing.Protocol</span></code></a> for up-to-date specs and documentation. Canonical typing specs are maintained at the <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/">typing specs site</a>; runtime typing behaviour is described in the CPython documentation.</p>
|
||
<p class="close-button">×</p>
|
||
<p>See the <a class="reference external" href="https://typing.readthedocs.io/en/latest/spec/meta.html">typing specification update process</a> for how to propose changes to the typing spec.</p>
|
||
</div>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>Type hints introduced in <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> can be used to specify type metadata
|
||
for static type checkers and other third party tools. However, <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>
|
||
only specifies the semantics of <em>nominal</em> subtyping. In this PEP we specify
|
||
static and runtime semantics of protocol classes that will provide a support
|
||
for <em>structural</em> subtyping (static duck typing).</p>
|
||
</section>
|
||
<section id="rationale-and-goals">
|
||
<span id="pep-544-rationale"></span><h2><a class="toc-backref" href="#rationale-and-goals" role="doc-backlink">Rationale and Goals</a></h2>
|
||
<p>Currently, <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> and the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module <a class="reference internal" href="#typing" id="id1"><span>[typing]</span></a> define abstract
|
||
base classes for several common Python protocols such as <code class="docutils literal notranslate"><span class="pre">Iterable</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">Sized</span></code>. The problem with them is that a class has to be explicitly marked
|
||
to support them, which is unpythonic and unlike what one would
|
||
normally do in idiomatic dynamically typed Python code. For example,
|
||
this conforms to <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Sized</span><span class="p">,</span> <span class="n">Iterable</span><span class="p">,</span> <span class="n">Iterator</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Bucket</span><span class="p">(</span><span class="n">Sized</span><span class="p">,</span> <span class="n">Iterable</span><span class="p">[</span><span class="nb">int</span><span class="p">]):</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Iterator</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The same problem appears with user-defined ABCs: they must be explicitly
|
||
subclassed or registered. This is particularly difficult to do with library
|
||
types as the type objects may be hidden deep in the implementation
|
||
of the library. Also, extensive use of ABCs might impose additional
|
||
runtime costs.</p>
|
||
<p>The intention of this PEP is to solve all these problems
|
||
by allowing users to write the above code without explicit base classes in
|
||
the class definition, allowing <code class="docutils literal notranslate"><span class="pre">Bucket</span></code> to be implicitly considered
|
||
a subtype of both <code class="docutils literal notranslate"><span class="pre">Sized</span></code> and <code class="docutils literal notranslate"><span class="pre">Iterable[int]</span></code> by static type checkers
|
||
using structural <a class="reference internal" href="#wiki-structural" id="id2"><span>[wiki-structural]</span></a> subtyping:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Iterator</span><span class="p">,</span> <span class="n">Iterable</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Bucket</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Iterator</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span> <span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">collect</span><span class="p">(</span><span class="n">items</span><span class="p">:</span> <span class="n">Iterable</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>
|
||
<span class="n">result</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">collect</span><span class="p">(</span><span class="n">Bucket</span><span class="p">())</span> <span class="c1"># Passes type check</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that ABCs in <code class="docutils literal notranslate"><span class="pre">typing</span></code> module already provide structural behavior
|
||
at runtime, <code class="docutils literal notranslate"><span class="pre">isinstance(Bucket(),</span> <span class="pre">Iterable)</span></code> returns <code class="docutils literal notranslate"><span class="pre">True</span></code>.
|
||
The main goal of this proposal is to support such behavior statically.
|
||
The same functionality will be provided for user-defined protocols, as
|
||
specified below. The above code with a protocol class matches common Python
|
||
conventions much better. It is also automatically extensible and works
|
||
with additional, unrelated classes that happen to implement
|
||
the required protocol.</p>
|
||
<section id="nominal-vs-structural-subtyping">
|
||
<h3><a class="toc-backref" href="#nominal-vs-structural-subtyping" role="doc-backlink">Nominal vs structural subtyping</a></h3>
|
||
<p>Structural subtyping is natural for Python programmers since it matches
|
||
the runtime semantics of duck typing: an object that has certain properties
|
||
is treated independently of its actual runtime class.
|
||
However, as discussed in <a class="pep reference internal" href="../pep-0483/" title="PEP 483 – The Theory of Type Hints">PEP 483</a>, both nominal and structural
|
||
subtyping have their strengths and weaknesses. Therefore, in this PEP we
|
||
<em>do not propose</em> to replace the nominal subtyping described by <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> with
|
||
structural subtyping completely. Instead, protocol classes as specified in
|
||
this PEP complement normal classes, and users are free to choose
|
||
where to apply a particular solution. See section on <a class="reference internal" href="#pep-544-rejected">rejected</a> ideas at the end of this PEP for additional motivation.</p>
|
||
</section>
|
||
<section id="non-goals">
|
||
<h3><a class="toc-backref" href="#non-goals" role="doc-backlink">Non-goals</a></h3>
|
||
<p>At runtime, protocol classes will be simple ABCs. There is no intent to
|
||
provide sophisticated runtime instance and class checks against protocol
|
||
classes. This would be difficult and error-prone and will contradict the logic
|
||
of <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>. As well, following <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> and <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a> we state that protocols are
|
||
<strong>completely optional</strong>:</p>
|
||
<ul class="simple">
|
||
<li>No runtime semantics will be imposed for variables or parameters annotated
|
||
with a protocol class.</li>
|
||
<li>Any checks will be performed only by third-party type checkers and
|
||
other tools.</li>
|
||
<li>Programmers are free to not use them even if they use type annotations.</li>
|
||
<li>There is no intent to make protocols non-optional in the future.</li>
|
||
</ul>
|
||
<p>To reiterate, providing complex runtime semantics for protocol classes
|
||
is not a goal of this PEP, the main goal is to provide a support and standards
|
||
for <em>static</em> structural subtyping. The possibility to use protocols
|
||
in the runtime context as ABCs is rather a minor bonus that exists mostly
|
||
to provide a seamless transition for projects that already use ABCs.</p>
|
||
</section>
|
||
</section>
|
||
<section id="existing-approaches-to-structural-subtyping">
|
||
<h2><a class="toc-backref" href="#existing-approaches-to-structural-subtyping" role="doc-backlink">Existing Approaches to Structural Subtyping</a></h2>
|
||
<p>Before describing the actual specification, we review and comment on existing
|
||
approaches related to structural subtyping in Python and other languages:</p>
|
||
<ul>
|
||
<li><code class="docutils literal notranslate"><span class="pre">zope.interface</span></code> <a class="reference internal" href="#zope-interfaces" id="id3"><span>[zope-interfaces]</span></a> was one of the first widely used
|
||
approaches to structural subtyping in Python. It is implemented by providing
|
||
special classes to distinguish interface classes from normal classes,
|
||
to mark interface attributes, and to explicitly declare implementation.
|
||
For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">zope.interface</span> <span class="kn">import</span> <span class="n">Interface</span><span class="p">,</span> <span class="n">Attribute</span><span class="p">,</span> <span class="n">implementer</span>
|
||
|
||
<span class="k">class</span> <span class="nc">IEmployee</span><span class="p">(</span><span class="n">Interface</span><span class="p">):</span>
|
||
|
||
<span class="n">name</span> <span class="o">=</span> <span class="n">Attribute</span><span class="p">(</span><span class="s2">"Name of employee"</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">do</span><span class="p">(</span><span class="n">work</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""Do some work"""</span>
|
||
|
||
<span class="nd">@implementer</span><span class="p">(</span><span class="n">IEmployee</span><span class="p">)</span>
|
||
<span class="k">class</span> <span class="nc">Employee</span><span class="p">:</span>
|
||
|
||
<span class="n">name</span> <span class="o">=</span> <span class="s1">'Anonymous'</span>
|
||
|
||
<span class="k">def</span> <span class="nf">do</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">work</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">work</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Zope interfaces support various contracts and constraints for interface
|
||
classes. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">zope.interface</span> <span class="kn">import</span> <span class="n">invariant</span>
|
||
|
||
<span class="k">def</span> <span class="nf">required_contact</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">email</span> <span class="ow">or</span> <span class="n">obj</span><span class="o">.</span><span class="n">phone</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">"At least one contact info is required"</span><span class="p">)</span>
|
||
|
||
<span class="k">class</span> <span class="nc">IPerson</span><span class="p">(</span><span class="n">Interface</span><span class="p">):</span>
|
||
|
||
<span class="n">name</span> <span class="o">=</span> <span class="n">Attribute</span><span class="p">(</span><span class="s2">"Name"</span><span class="p">)</span>
|
||
<span class="n">email</span> <span class="o">=</span> <span class="n">Attribute</span><span class="p">(</span><span class="s2">"Email Address"</span><span class="p">)</span>
|
||
<span class="n">phone</span> <span class="o">=</span> <span class="n">Attribute</span><span class="p">(</span><span class="s2">"Phone Number"</span><span class="p">)</span>
|
||
|
||
<span class="n">invariant</span><span class="p">(</span><span class="n">required_contact</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Even more detailed invariants are supported. However, Zope interfaces rely
|
||
entirely on runtime validation. Such focus on runtime properties goes
|
||
beyond the scope of the current proposal, and static support for invariants
|
||
might be difficult to implement. However, the idea of marking an interface
|
||
class with a special base class is reasonable and easy to implement both
|
||
statically and at runtime.</p>
|
||
</li>
|
||
<li>Python abstract base classes <a class="reference internal" href="#abstract-classes" id="id4"><span>[abstract-classes]</span></a> are the standard
|
||
library tool to provide some functionality similar to structural subtyping.
|
||
The drawback of this approach is the necessity to either subclass
|
||
the abstract class or register an implementation explicitly:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">ABC</span>
|
||
|
||
<span class="k">class</span> <span class="nc">MyTuple</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
|
||
<span class="n">MyTuple</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="nb">tuple</span><span class="p">)</span>
|
||
|
||
<span class="k">assert</span> <span class="nb">issubclass</span><span class="p">(</span><span class="nb">tuple</span><span class="p">,</span> <span class="n">MyTuple</span><span class="p">)</span>
|
||
<span class="k">assert</span> <span class="nb">isinstance</span><span class="p">((),</span> <span class="n">MyTuple</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As mentioned in the <a class="reference internal" href="#pep-544-rationale">rationale</a>,
|
||
we want to avoid such necessity, especially in static context.
|
||
However, in a runtime context, ABCs are good candidates for
|
||
protocol classes and they are already used extensively in
|
||
the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module.</p>
|
||
</li>
|
||
<li>Abstract classes defined in <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code> module <a class="reference internal" href="#collections-abc" id="id5"><span>[collections-abc]</span></a>
|
||
are slightly more advanced since they implement a custom
|
||
<code class="docutils literal notranslate"><span class="pre">__subclasshook__()</span></code> method that allows runtime structural checks without
|
||
explicit registration:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">collections.abc</span> <span class="kn">import</span> <span class="n">Iterable</span>
|
||
|
||
<span class="k">class</span> <span class="nc">MyIterable</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">[]</span>
|
||
|
||
<span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">MyIterable</span><span class="p">(),</span> <span class="n">Iterable</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Such behavior seems to be a perfect fit for both runtime and static behavior
|
||
of protocols. As discussed in <a class="reference internal" href="#pep-544-rationale">rationale</a>,
|
||
we propose to add static support for such behavior.
|
||
In addition, to allow users to achieve such runtime
|
||
behavior for <em>user-defined</em> protocols a special <code class="docutils literal notranslate"><span class="pre">@runtime_checkable</span></code> decorator
|
||
will be provided, see detailed <a class="reference internal" href="#discussion">discussion</a> below.</p>
|
||
</li>
|
||
<li>TypeScript <a class="reference internal" href="#typescript" id="id6"><span>[typescript]</span></a> provides support for user-defined classes and
|
||
interfaces. Explicit implementation declaration is not required and
|
||
structural subtyping is verified statically. For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>interface LabeledItem {
|
||
label: string;
|
||
size?: int;
|
||
}
|
||
|
||
function printLabel(obj: LabeledItem) {
|
||
console.log(obj.label);
|
||
}
|
||
|
||
let myObj = {size: 10, label: "Size 10 Object"};
|
||
printLabel(myObj);
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that optional interface members are supported. Also, TypeScript
|
||
prohibits redundant members in implementations. While the idea of
|
||
optional members looks interesting, it would complicate this proposal and
|
||
it is not clear how useful it will be. Therefore, it is proposed to postpone
|
||
this; see <a class="reference internal" href="#pep-544-rejected">rejected</a> ideas. In general, the idea of
|
||
static protocol checking without runtime implications looks reasonable,
|
||
and basically this proposal follows the same line.</p>
|
||
</li>
|
||
<li>Go <a class="reference internal" href="#golang" id="id7"><span>[golang]</span></a> uses a more radical approach and makes interfaces the primary
|
||
way to provide type information. Also, assignments are used to explicitly
|
||
ensure implementation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span> <span class="n">SomeInterface</span> <span class="n">interface</span> <span class="p">{</span>
|
||
<span class="n">SomeMethod</span><span class="p">()</span> <span class="p">([]</span><span class="n">byte</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">someval</span><span class="o">.</span><span class="p">(</span><span class="n">SomeInterface</span><span class="p">);</span> <span class="n">ok</span> <span class="p">{</span>
|
||
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s2">"value implements some interface"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Both these ideas are questionable in the context of this proposal. See
|
||
the section on <a class="reference internal" href="#pep-544-rejected">rejected</a> ideas.</p>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<section id="terminology">
|
||
<h3><a class="toc-backref" href="#terminology" role="doc-backlink">Terminology</a></h3>
|
||
<p>We propose to use the term <em>protocols</em> for types supporting structural
|
||
subtyping. The reason is that the term <em>iterator protocol</em>,
|
||
for example, is widely understood in the community, and coming up with
|
||
a new term for this concept in a statically typed context would just create
|
||
confusion.</p>
|
||
<p>This has the drawback that the term <em>protocol</em> becomes overloaded with
|
||
two subtly different meanings: the first is the traditional, well-known but
|
||
slightly fuzzy concept of protocols such as iterator; the second is the more
|
||
explicitly defined concept of protocols in statically typed code.
|
||
The distinction is not important most of the time, and in other
|
||
cases we propose to just add a qualifier such as <em>protocol classes</em>
|
||
when referring to the static type concept.</p>
|
||
<p>If a class includes a protocol in its MRO, the class is called
|
||
an <em>explicit</em> subclass of the protocol. If a class is a structural subtype
|
||
of a protocol, it is said to implement the protocol and to be compatible
|
||
with a protocol. If a class is compatible with a protocol but the protocol
|
||
is not included in the MRO, the class is an <em>implicit</em> subtype
|
||
of the protocol. (Note that one can explicitly subclass a protocol and
|
||
still not implement it if a protocol attribute is set to <code class="docutils literal notranslate"><span class="pre">None</span></code>
|
||
in the subclass, see Python <a class="reference internal" href="#data-model" id="id8"><span>[data-model]</span></a> for details.)</p>
|
||
<p>The attributes (variables and methods) of a protocol that are mandatory
|
||
for other class in order to be considered a structural subtype are called
|
||
protocol members.</p>
|
||
</section>
|
||
<section id="defining-a-protocol">
|
||
<span id="definition"></span><h3><a class="toc-backref" href="#defining-a-protocol" role="doc-backlink">Defining a protocol</a></h3>
|
||
<p>Protocols are defined by including a special new class <code class="docutils literal notranslate"><span class="pre">typing.Protocol</span></code>
|
||
(an instance of <code class="docutils literal notranslate"><span class="pre">abc.ABCMeta</span></code>) in the base classes list, typically
|
||
at the end of the list. Here is a simple example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">SupportsClose</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Now if one defines a class <code class="docutils literal notranslate"><span class="pre">Resource</span></code> with a <code class="docutils literal notranslate"><span class="pre">close()</span></code> method that has
|
||
a compatible signature, it would implicitly be a subtype of
|
||
<code class="docutils literal notranslate"><span class="pre">SupportsClose</span></code>, since the structural subtyping is used for
|
||
protocol types:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Resource</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">file</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lock</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Apart from few restrictions explicitly mentioned below, protocol types can
|
||
be used in every context where a normal types can:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">close_all</span><span class="p">(</span><span class="n">things</span><span class="p">:</span> <span class="n">Iterable</span><span class="p">[</span><span class="n">SupportsClose</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">things</span><span class="p">:</span>
|
||
<span class="n">t</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
|
||
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'foo.txt'</span><span class="p">)</span>
|
||
<span class="n">r</span> <span class="o">=</span> <span class="n">Resource</span><span class="p">()</span>
|
||
<span class="n">close_all</span><span class="p">([</span><span class="n">f</span><span class="p">,</span> <span class="n">r</span><span class="p">])</span> <span class="c1"># OK!</span>
|
||
<span class="n">close_all</span><span class="p">([</span><span class="mi">1</span><span class="p">])</span> <span class="c1"># Error: 'int' has no 'close' method</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that both the user-defined class <code class="docutils literal notranslate"><span class="pre">Resource</span></code> and the built-in
|
||
<code class="docutils literal notranslate"><span class="pre">IO</span></code> type (the return type of <code class="docutils literal notranslate"><span class="pre">open()</span></code>) are considered subtypes of
|
||
<code class="docutils literal notranslate"><span class="pre">SupportsClose</span></code>, because they provide a <code class="docutils literal notranslate"><span class="pre">close()</span></code> method with
|
||
a compatible type signature.</p>
|
||
</section>
|
||
<section id="protocol-members">
|
||
<h3><a class="toc-backref" href="#protocol-members" role="doc-backlink">Protocol members</a></h3>
|
||
<p>All methods defined in the protocol class body are protocol members, both
|
||
normal and decorated with <code class="docutils literal notranslate"><span class="pre">@abstractmethod</span></code>. If any parameters of a
|
||
protocol method are not annotated, then their types are assumed to be <code class="docutils literal notranslate"><span class="pre">Any</span></code>
|
||
(see <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>). Bodies of protocol methods are type checked.
|
||
An abstract method that should not be called via <code class="docutils literal notranslate"><span class="pre">super()</span></code> ought to raise
|
||
<code class="docutils literal notranslate"><span class="pre">NotImplementedError</span></code>. Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Protocol</span>
|
||
<span class="kn">from</span> <span class="nn">abc</span> <span class="kn">import</span> <span class="n">abstractmethod</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Example</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">first</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="c1"># This is a protocol member</span>
|
||
<span class="k">return</span> <span class="mi">42</span>
|
||
|
||
<span class="nd">@abstractmethod</span>
|
||
<span class="k">def</span> <span class="nf">second</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="c1"># Method without a default implementation</span>
|
||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Static methods, class methods, and properties are equally allowed
|
||
in protocols.</p>
|
||
<p>To define a protocol variable, one can use <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a> variable
|
||
annotations in the class body. Additional attributes <em>only</em> defined in
|
||
the body of a method by assignment via <code class="docutils literal notranslate"><span class="pre">self</span></code> are not allowed. The rationale
|
||
for this is that the protocol class implementation is often not shared by
|
||
subtypes, so the interface should not depend on the default implementation.
|
||
Examples:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Protocol</span><span class="p">,</span> <span class="n">List</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Template</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># This is a protocol member</span>
|
||
<span class="n">value</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># This one too (with default)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">temp</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># Error in type checker</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Concrete</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">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
|
||
|
||
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span>
|
||
|
||
<span class="n">var</span><span class="p">:</span> <span class="n">Template</span> <span class="o">=</span> <span class="n">Concrete</span><span class="p">(</span><span class="s1">'value'</span><span class="p">,</span> <span class="mi">42</span><span class="p">)</span> <span class="c1"># OK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To distinguish between protocol class variables and protocol instance
|
||
variables, the special <code class="docutils literal notranslate"><span class="pre">ClassVar</span></code> annotation should be used as specified
|
||
by <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a>. By default, protocol variables as defined above are considered
|
||
readable and writable. To define a read-only protocol variable, one can use
|
||
an (abstract) property.</p>
|
||
</section>
|
||
<section id="explicitly-declaring-implementation">
|
||
<h3><a class="toc-backref" href="#explicitly-declaring-implementation" role="doc-backlink">Explicitly declaring implementation</a></h3>
|
||
<p>To explicitly declare that a certain class implements a given protocol,
|
||
it can be used as a regular base class. In this case a class could use
|
||
default implementations of protocol members. Static analysis tools are
|
||
expected to automatically detect that a class implements a given protocol.
|
||
So while it’s possible to subclass a protocol explicitly, it’s <em>not necessary</em>
|
||
to do so for the sake of type-checking.</p>
|
||
<p>The default implementations cannot be used if
|
||
the subtype relationship is implicit and only via structural
|
||
subtyping – the semantics of inheritance is not changed. Examples:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">PColor</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="nd">@abstractmethod</span>
|
||
<span class="k">def</span> <span class="nf">draw</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">complex_method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="c1"># some complex code here</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">NiceColor</span><span class="p">(</span><span class="n">PColor</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">draw</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"deep blue"</span>
|
||
|
||
<span class="k">class</span> <span class="nc">BadColor</span><span class="p">(</span><span class="n">PColor</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">draw</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span> <span class="c1"># Error, no default implementation</span>
|
||
|
||
<span class="k">class</span> <span class="nc">ImplicitColor</span><span class="p">:</span> <span class="c1"># Note no 'PColor' base here</span>
|
||
<span class="k">def</span> <span class="nf">draw</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="s2">"probably gray"</span>
|
||
<span class="k">def</span> <span class="nf">complex_method</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="c1"># class needs to implement this</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">nice</span><span class="p">:</span> <span class="n">NiceColor</span>
|
||
<span class="n">another</span><span class="p">:</span> <span class="n">ImplicitColor</span>
|
||
|
||
<span class="k">def</span> <span class="nf">represent</span><span class="p">(</span><span class="n">c</span><span class="p">:</span> <span class="n">PColor</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">draw</span><span class="p">(),</span> <span class="n">c</span><span class="o">.</span><span class="n">complex_method</span><span class="p">())</span>
|
||
|
||
<span class="n">represent</span><span class="p">(</span><span class="n">nice</span><span class="p">)</span> <span class="c1"># OK</span>
|
||
<span class="n">represent</span><span class="p">(</span><span class="n">another</span><span class="p">)</span> <span class="c1"># Also OK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that there is little difference between explicit and implicit
|
||
subtypes, the main benefit of explicit subclassing is to get some protocol
|
||
methods “for free”. In addition, type checkers can statically verify that
|
||
the class actually implements the protocol correctly:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">RGB</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="n">rgb</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">]</span>
|
||
|
||
<span class="nd">@abstractmethod</span>
|
||
<span class="k">def</span> <span class="nf">intensity</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="mi">0</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Point</span><span class="p">(</span><span class="n">RGB</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">red</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">green</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">blue</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">rgb</span> <span class="o">=</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span> <span class="c1"># Error, 'blue' must be 'int'</span>
|
||
|
||
<span class="c1"># Type checker might warn that 'intensity' is not defined</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A class can explicitly inherit from multiple protocols and also from normal
|
||
classes. In this case methods are resolved using normal MRO and a type checker
|
||
verifies that all subtyping are correct. The semantics of <code class="docutils literal notranslate"><span class="pre">@abstractmethod</span></code>
|
||
is not changed, all of them must be implemented by an explicit subclass
|
||
before it can be instantiated.</p>
|
||
</section>
|
||
<section id="merging-and-extending-protocols">
|
||
<h3><a class="toc-backref" href="#merging-and-extending-protocols" role="doc-backlink">Merging and extending protocols</a></h3>
|
||
<p>The general philosophy is that protocols are mostly like regular ABCs,
|
||
but a static type checker will handle them specially. Subclassing a protocol
|
||
class would not turn the subclass into a protocol unless it also has
|
||
<code class="docutils literal notranslate"><span class="pre">typing.Protocol</span></code> as an explicit base class. Without this base, the class
|
||
is “downgraded” to a regular ABC that cannot be used with structural
|
||
subtyping. The rationale for this rule is that we don’t want to accidentally
|
||
have some class act as a protocol just because one of its base classes
|
||
happens to be one. We still slightly prefer nominal subtyping over structural
|
||
subtyping in the static typing world.</p>
|
||
<p>A subprotocol can be defined by having <em>both</em> one or more protocols as
|
||
immediate base classes and also having <code class="docutils literal notranslate"><span class="pre">typing.Protocol</span></code> as an immediate
|
||
base class:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Sized</span><span class="p">,</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">SizedAndClosable</span><span class="p">(</span><span class="n">Sized</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Now the protocol <code class="docutils literal notranslate"><span class="pre">SizedAndClosable</span></code> is a protocol with two methods,
|
||
<code class="docutils literal notranslate"><span class="pre">__len__</span></code> and <code class="docutils literal notranslate"><span class="pre">close</span></code>. If one omits <code class="docutils literal notranslate"><span class="pre">Protocol</span></code> in the base class list,
|
||
this would be a regular (non-protocol) class that must implement <code class="docutils literal notranslate"><span class="pre">Sized</span></code>.
|
||
Alternatively, one can implement <code class="docutils literal notranslate"><span class="pre">SizedAndClosable</span></code> protocol by merging
|
||
the <code class="docutils literal notranslate"><span class="pre">SupportsClose</span></code> protocol from the example in the <a class="reference internal" href="#definition">definition</a> section
|
||
with <code class="docutils literal notranslate"><span class="pre">typing.Sized</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Sized</span>
|
||
|
||
<span class="k">class</span> <span class="nc">SupportsClose</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">SizedAndClosable</span><span class="p">(</span><span class="n">Sized</span><span class="p">,</span> <span class="n">SupportsClose</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The two definitions of <code class="docutils literal notranslate"><span class="pre">SizedAndClosable</span></code> are equivalent.
|
||
Subclass relationships between protocols are not meaningful when
|
||
considering subtyping, since structural compatibility is
|
||
the criterion, not the MRO.</p>
|
||
<p>If <code class="docutils literal notranslate"><span class="pre">Protocol</span></code> is included in the base class list, all the other base classes
|
||
must be protocols. A protocol can’t extend a regular class, see <a class="reference internal" href="#pep-544-rejected">rejected</a> ideas for rationale.
|
||
Note that rules around explicit subclassing are different
|
||
from regular ABCs, where abstractness is simply defined by having at least one
|
||
abstract method being unimplemented. Protocol classes must be marked
|
||
<em>explicitly</em>.</p>
|
||
</section>
|
||
<section id="generic-protocols">
|
||
<h3><a class="toc-backref" href="#generic-protocols" role="doc-backlink">Generic protocols</a></h3>
|
||
<p>Generic protocols are important. For example, <code class="docutils literal notranslate"><span class="pre">SupportsAbs</span></code>, <code class="docutils literal notranslate"><span class="pre">Iterable</span></code>
|
||
and <code class="docutils literal notranslate"><span class="pre">Iterator</span></code> are generic protocols. They are defined similar to normal
|
||
non-protocol generic types:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Iterable</span><span class="p">(</span><span class="n">Protocol</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
||
<span class="nd">@abstractmethod</span>
|
||
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Iterator</span><span class="p">[</span><span class="n">T</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">Protocol[T,</span> <span class="pre">S,</span> <span class="pre">...]</span></code> is allowed as a shorthand for
|
||
<code class="docutils literal notranslate"><span class="pre">Protocol,</span> <span class="pre">Generic[T,</span> <span class="pre">S,</span> <span class="pre">...]</span></code>.</p>
|
||
<p>User-defined generic protocols support explicitly declared variance.
|
||
Type checkers will warn if the inferred variance is different from
|
||
the declared variance. Examples:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">)</span>
|
||
<span class="n">T_co</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T_co'</span><span class="p">,</span> <span class="n">covariant</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="n">T_contra</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T_contra'</span><span class="p">,</span> <span class="n">contravariant</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Box</span><span class="p">(</span><span class="n">Protocol</span><span class="p">[</span><span class="n">T_co</span><span class="p">]):</span>
|
||
<span class="k">def</span> <span class="nf">content</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">T_co</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">box</span><span class="p">:</span> <span class="n">Box</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
|
||
<span class="n">second_box</span><span class="p">:</span> <span class="n">Box</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
|
||
<span class="n">box</span> <span class="o">=</span> <span class="n">second_box</span> <span class="c1"># This is OK due to the covariance of 'Box'.</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Sender</span><span class="p">(</span><span class="n">Protocol</span><span class="p">[</span><span class="n">T_contra</span><span class="p">]):</span>
|
||
<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">T_contra</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">sender</span><span class="p">:</span> <span class="n">Sender</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
|
||
<span class="n">new_sender</span><span class="p">:</span> <span class="n">Sender</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
|
||
<span class="n">new_sender</span> <span class="o">=</span> <span class="n">sender</span> <span class="c1"># OK, 'Sender' is contravariant.</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Proto</span><span class="p">(</span><span class="n">Protocol</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
||
<span class="n">attr</span><span class="p">:</span> <span class="n">T</span> <span class="c1"># this class is invariant, since it has a mutable attribute</span>
|
||
|
||
<span class="n">var</span><span class="p">:</span> <span class="n">Proto</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
|
||
<span class="n">another_var</span><span class="p">:</span> <span class="n">Proto</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
|
||
<span class="n">var</span> <span class="o">=</span> <span class="n">another_var</span> <span class="c1"># Error! 'Proto[float]' is incompatible with 'Proto[int]'.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that unlike nominal classes, de facto covariant protocols cannot be
|
||
declared as invariant, since this can break transitivity of subtyping
|
||
(see <a class="reference internal" href="#pep-544-rejected">rejected</a> ideas for details). For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">)</span>
|
||
|
||
<span class="k">class</span> <span class="nc">AnotherBox</span><span class="p">(</span><span class="n">Protocol</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span> <span class="c1"># Error, this protocol is covariant in T,</span>
|
||
<span class="k">def</span> <span class="nf">content</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">T</span><span class="p">:</span> <span class="c1"># not invariant.</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="recursive-protocols">
|
||
<h3><a class="toc-backref" href="#recursive-protocols" role="doc-backlink">Recursive protocols</a></h3>
|
||
<p>Recursive protocols are also supported. Forward references to the protocol
|
||
class names can be given as strings as specified by <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>. Recursive
|
||
protocols are useful for representing self-referential data structures
|
||
like trees in an abstract fashion:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Traversable</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">leaves</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Iterable</span><span class="p">[</span><span class="s1">'Traversable'</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that for recursive protocols, a class is considered a subtype of
|
||
the protocol in situations where the decision depends on itself.
|
||
Continuing the previous example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">SimpleTree</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">leaves</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="s1">'SimpleTree'</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">root</span><span class="p">:</span> <span class="n">Traversable</span> <span class="o">=</span> <span class="n">SimpleTree</span><span class="p">()</span> <span class="c1"># OK</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Tree</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="k">def</span> <span class="nf">leaves</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="s1">'Tree[T]'</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">walk</span><span class="p">(</span><span class="n">graph</span><span class="p">:</span> <span class="n">Traversable</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="n">tree</span><span class="p">:</span> <span class="n">Tree</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span> <span class="o">=</span> <span class="n">Tree</span><span class="p">()</span>
|
||
<span class="n">walk</span><span class="p">(</span><span class="n">tree</span><span class="p">)</span> <span class="c1"># OK, 'Tree[float]' is a subtype of 'Traversable'</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="self-types-in-protocols">
|
||
<h3><a class="toc-backref" href="#self-types-in-protocols" role="doc-backlink">Self-types in protocols</a></h3>
|
||
<p>The self-types in protocols follow the
|
||
<a class="pep reference internal" href="../pep-0484/#annotating-instance-and-class-methods" title="PEP 484 – Type Hints § Annotating instance and class methods">corresponding specification</a>
|
||
of <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">C</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'C'</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="s1">'Copyable'</span><span class="p">)</span>
|
||
<span class="k">class</span> <span class="nc">Copyable</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">self</span><span class="p">:</span> <span class="n">C</span><span class="p">)</span> <span class="o">-></span> <span class="n">C</span><span class="p">:</span>
|
||
|
||
<span class="k">class</span> <span class="nc">One</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="s1">'One'</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="s1">'Other'</span><span class="p">)</span>
|
||
<span class="k">class</span> <span class="nc">Other</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">self</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="o">-></span> <span class="n">T</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">c</span><span class="p">:</span> <span class="n">Copyable</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">One</span><span class="p">()</span> <span class="c1"># OK</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">Other</span><span class="p">()</span> <span class="c1"># Also OK</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="callback-protocols">
|
||
<h3><a class="toc-backref" href="#callback-protocols" role="doc-backlink">Callback protocols</a></h3>
|
||
<p>Protocols can be used to define flexible callback types that are hard
|
||
(or even impossible) to express using the <code class="docutils literal notranslate"><span class="pre">Callable[...]</span></code> syntax
|
||
specified by <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>, such as variadic, overloaded, and complex generic
|
||
callbacks. They can be defined as protocols with a <code class="docutils literal notranslate"><span class="pre">__call__</span></code> member:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Combiner</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">vals</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span>
|
||
<span class="n">maxlen</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">bytes</span><span class="p">]:</span> <span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">good_cb</span><span class="p">(</span><span class="o">*</span><span class="n">vals</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">maxlen</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">bytes</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">bad_cb</span><span class="p">(</span><span class="o">*</span><span class="n">vals</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">maxitems</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="nb">bytes</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">comb</span><span class="p">:</span> <span class="n">Combiner</span> <span class="o">=</span> <span class="n">good_cb</span> <span class="c1"># OK</span>
|
||
<span class="n">comb</span> <span class="o">=</span> <span class="n">bad_cb</span> <span class="c1"># Error! Argument 2 has incompatible type because of</span>
|
||
<span class="c1"># different name and kind in the callback</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Callback protocols and <code class="docutils literal notranslate"><span class="pre">Callable[...]</span></code> types can be used interchangeably.</p>
|
||
</section>
|
||
</section>
|
||
<section id="using-protocols">
|
||
<h2><a class="toc-backref" href="#using-protocols" role="doc-backlink">Using Protocols</a></h2>
|
||
<section id="subtyping-relationships-with-other-types">
|
||
<h3><a class="toc-backref" href="#subtyping-relationships-with-other-types" role="doc-backlink">Subtyping relationships with other types</a></h3>
|
||
<p>Protocols cannot be instantiated, so there are no values whose
|
||
runtime type is a protocol. For variables and parameters with protocol types,
|
||
subtyping relationships are subject to the following rules:</p>
|
||
<ul class="simple">
|
||
<li>A protocol is never a subtype of a concrete type.</li>
|
||
<li>A concrete type <code class="docutils literal notranslate"><span class="pre">X</span></code> is a subtype of protocol <code class="docutils literal notranslate"><span class="pre">P</span></code>
|
||
if and only if <code class="docutils literal notranslate"><span class="pre">X</span></code> implements all protocol members of <code class="docutils literal notranslate"><span class="pre">P</span></code> with
|
||
compatible types. In other words, subtyping with respect to a protocol is
|
||
always structural.</li>
|
||
<li>A protocol <code class="docutils literal notranslate"><span class="pre">P1</span></code> is a subtype of another protocol <code class="docutils literal notranslate"><span class="pre">P2</span></code> if <code class="docutils literal notranslate"><span class="pre">P1</span></code> defines
|
||
all protocol members of <code class="docutils literal notranslate"><span class="pre">P2</span></code> with compatible types.</li>
|
||
</ul>
|
||
<p>Generic protocol types follow the same rules of variance as non-protocol
|
||
types. Protocol types can be used in all contexts where any other types
|
||
can be used, such as in <code class="docutils literal notranslate"><span class="pre">Union</span></code>, <code class="docutils literal notranslate"><span class="pre">ClassVar</span></code>, type variables bounds, etc.
|
||
Generic protocols follow the rules for generic abstract classes, except for
|
||
using structural compatibility instead of compatibility defined by
|
||
inheritance relationships.</p>
|
||
<p>Static type checkers will recognize protocol implementations, even if the
|
||
corresponding protocols are <em>not imported</em>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># file lib.py</span>
|
||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Sized</span>
|
||
|
||
<span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">,</span> <span class="n">contravariant</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
<span class="k">class</span> <span class="nc">ListLike</span><span class="p">(</span><span class="n">Sized</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span>
|
||
<span class="k">def</span> <span class="nf">append</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
|
||
<span class="k">def</span> <span class="nf">populate</span><span class="p">(</span><span class="n">lst</span><span class="p">:</span> <span class="n">ListLike</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="c1"># file main.py</span>
|
||
<span class="kn">from</span> <span class="nn">lib</span> <span class="kn">import</span> <span class="n">populate</span> <span class="c1"># Note that ListLike is NOT imported</span>
|
||
|
||
<span class="k">class</span> <span class="nc">MockStack</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="mi">42</span>
|
||
<span class="k">def</span> <span class="nf">append</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
|
||
<span class="n">populate</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="c1"># Passes type check</span>
|
||
<span class="n">populate</span><span class="p">(</span><span class="n">MockStack</span><span class="p">())</span> <span class="c1"># Also OK</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="unions-and-intersections-of-protocols">
|
||
<h3><a class="toc-backref" href="#unions-and-intersections-of-protocols" role="doc-backlink">Unions and intersections of protocols</a></h3>
|
||
<p><code class="docutils literal notranslate"><span class="pre">Union</span></code> of protocol classes behaves the same way as for non-protocol
|
||
classes. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Union</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Exitable</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">exit</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">class</span> <span class="nc">Quittable</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">quit</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">finish</span><span class="p">(</span><span class="n">task</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n">Exitable</span><span class="p">,</span> <span class="n">Quittable</span><span class="p">])</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">class</span> <span class="nc">DefaultJob</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">quit</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="mi">0</span>
|
||
<span class="n">finish</span><span class="p">(</span><span class="n">DefaultJob</span><span class="p">())</span> <span class="c1"># OK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>One can use multiple inheritance to define an intersection of protocols.
|
||
Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Iterable</span><span class="p">,</span> <span class="n">Hashable</span>
|
||
|
||
<span class="k">class</span> <span class="nc">HashableFloats</span><span class="p">(</span><span class="n">Iterable</span><span class="p">[</span><span class="nb">float</span><span class="p">],</span> <span class="n">Hashable</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
|
||
<span class="k">def</span> <span class="nf">cached_func</span><span class="p">(</span><span class="n">args</span><span class="p">:</span> <span class="n">HashableFloats</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="n">cached_func</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="c1"># OK, tuple is both hashable and iterable</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If this will prove to be a widely used scenario, then a special
|
||
intersection type construct could be added in future as specified by <a class="pep reference internal" href="../pep-0483/" title="PEP 483 – The Theory of Type Hints">PEP 483</a>,
|
||
see <a class="reference internal" href="#pep-544-rejected">rejected</a> ideas for more details.</p>
|
||
</section>
|
||
<section id="type-and-class-objects-vs-protocols">
|
||
<h3><a class="toc-backref" href="#type-and-class-objects-vs-protocols" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">Type[]</span></code> and class objects vs protocols</a></h3>
|
||
<p>Variables and parameters annotated with <code class="docutils literal notranslate"><span class="pre">Type[Proto]</span></code> accept only concrete
|
||
(non-protocol) subtypes of <code class="docutils literal notranslate"><span class="pre">Proto</span></code>. The main reason for this is to allow
|
||
instantiation of parameters with such type. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Proto</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="nd">@abstractmethod</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">class</span> <span class="nc">Concrete</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="mi">42</span>
|
||
|
||
<span class="k">def</span> <span class="nf">fun</span><span class="p">(</span><span class="bp">cls</span><span class="p">:</span> <span class="n">Type</span><span class="p">[</span><span class="n">Proto</span><span class="p">])</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">cls</span><span class="p">()</span><span class="o">.</span><span class="n">meth</span><span class="p">()</span> <span class="c1"># OK</span>
|
||
<span class="n">fun</span><span class="p">(</span><span class="n">Proto</span><span class="p">)</span> <span class="c1"># Error</span>
|
||
<span class="n">fun</span><span class="p">(</span><span class="n">Concrete</span><span class="p">)</span> <span class="c1"># OK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The same rule applies to variables:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">var</span><span class="p">:</span> <span class="n">Type</span><span class="p">[</span><span class="n">Proto</span><span class="p">]</span>
|
||
<span class="n">var</span> <span class="o">=</span> <span class="n">Proto</span> <span class="c1"># Error</span>
|
||
<span class="n">var</span> <span class="o">=</span> <span class="n">Concrete</span> <span class="c1"># OK</span>
|
||
<span class="n">var</span><span class="p">()</span><span class="o">.</span><span class="n">meth</span><span class="p">()</span> <span class="c1"># OK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Assigning an ABC or a protocol class to a variable is allowed if it is
|
||
not explicitly typed, and such assignment creates a type alias.
|
||
For normal (non-abstract) classes, the behavior of <code class="docutils literal notranslate"><span class="pre">Type[]</span></code> is
|
||
not changed.</p>
|
||
<p>A class object is considered an implementation of a protocol if accessing
|
||
all members on it results in types compatible with the protocol members.
|
||
For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">ProtoA</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">class</span> <span class="nc">ProtoB</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">:</span> <span class="n">Any</span><span class="p">,</span> <span class="n">x</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>
|
||
|
||
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="n">a</span><span class="p">:</span> <span class="n">ProtoA</span> <span class="o">=</span> <span class="n">C</span> <span class="c1"># Type check error, signatures don't match!</span>
|
||
<span class="n">b</span><span class="p">:</span> <span class="n">ProtoB</span> <span class="o">=</span> <span class="n">C</span> <span class="c1"># OK</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="newtype-and-type-aliases">
|
||
<h3><a class="toc-backref" href="#newtype-and-type-aliases" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">NewType()</span></code> and type aliases</a></h3>
|
||
<p>Protocols are essentially anonymous. To emphasize this point, static type
|
||
checkers might refuse protocol classes inside <code class="docutils literal notranslate"><span class="pre">NewType()</span></code> to avoid an
|
||
illusion that a distinct type is provided:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">NewType</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">,</span> <span class="n">Iterator</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Id</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="n">code</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">secrets</span><span class="p">:</span> <span class="n">Iterator</span><span class="p">[</span><span class="nb">bytes</span><span class="p">]</span>
|
||
|
||
<span class="n">UserId</span> <span class="o">=</span> <span class="n">NewType</span><span class="p">(</span><span class="s1">'UserId'</span><span class="p">,</span> <span class="n">Id</span><span class="p">)</span> <span class="c1"># Error, can't provide distinct type</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In contrast, type aliases are fully supported, including generic type
|
||
aliases:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">TypeVar</span><span class="p">,</span> <span class="n">Reversible</span><span class="p">,</span> <span class="n">Iterable</span><span class="p">,</span> <span class="n">Sized</span>
|
||
|
||
<span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">)</span>
|
||
<span class="k">class</span> <span class="nc">SizedIterable</span><span class="p">(</span><span class="n">Iterable</span><span class="p">[</span><span class="n">T</span><span class="p">],</span> <span class="n">Sized</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
<span class="n">CompatReversible</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="n">Reversible</span><span class="p">[</span><span class="n">T</span><span class="p">],</span> <span class="n">SizedIterable</span><span class="p">[</span><span class="n">T</span><span class="p">]]</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="modules-as-implementations-of-protocols">
|
||
<h3><a class="toc-backref" href="#modules-as-implementations-of-protocols" role="doc-backlink">Modules as implementations of protocols</a></h3>
|
||
<p>A module object is accepted where a protocol is expected if the public
|
||
interface of the given module is compatible with the expected protocol.
|
||
For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># file default_config.py</span>
|
||
<span class="n">timeout</span> <span class="o">=</span> <span class="mi">100</span>
|
||
<span class="n">one_flag</span> <span class="o">=</span> <span class="kc">True</span>
|
||
<span class="n">other_flag</span> <span class="o">=</span> <span class="kc">False</span>
|
||
|
||
<span class="c1"># file main.py</span>
|
||
<span class="kn">import</span> <span class="nn">default_config</span>
|
||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Options</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="n">timeout</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">one_flag</span><span class="p">:</span> <span class="nb">bool</span>
|
||
<span class="n">other_flag</span><span class="p">:</span> <span class="nb">bool</span>
|
||
|
||
<span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="n">options</span><span class="p">:</span> <span class="n">Options</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">setup</span><span class="p">(</span><span class="n">default_config</span><span class="p">)</span> <span class="c1"># OK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To determine compatibility of module level functions, the <code class="docutils literal notranslate"><span class="pre">self</span></code> argument
|
||
of the corresponding protocol methods is dropped. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># callbacks.py</span>
|
||
<span class="k">def</span> <span class="nf">on_error</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">on_success</span><span class="p">()</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="c1"># main.py</span>
|
||
<span class="kn">import</span> <span class="nn">callbacks</span>
|
||
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Reporter</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">on_error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">def</span> <span class="nf">on_success</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">rp</span><span class="p">:</span> <span class="n">Reporter</span> <span class="o">=</span> <span class="n">callbacks</span> <span class="c1"># Passes type check</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="runtime-checkable-decorator-and-narrowing-types-by-isinstance">
|
||
<span id="discussion"></span><h3><a class="toc-backref" href="#runtime-checkable-decorator-and-narrowing-types-by-isinstance" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">@runtime_checkable</span></code> decorator and narrowing types by <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code></a></h3>
|
||
<p>The default semantics is that <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass()</span></code> fail
|
||
for protocol types. This is in the spirit of duck typing – protocols
|
||
basically would be used to model duck typing statically, not explicitly
|
||
at runtime.</p>
|
||
<p>However, it should be possible for protocol types to implement custom
|
||
instance and class checks when this makes sense, similar to how <code class="docutils literal notranslate"><span class="pre">Iterable</span></code>
|
||
and other ABCs in <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code> and <code class="docutils literal notranslate"><span class="pre">typing</span></code> already do it,
|
||
but this is limited to non-generic and unsubscripted generic protocols
|
||
(<code class="docutils literal notranslate"><span class="pre">Iterable</span></code> is statically equivalent to <code class="docutils literal notranslate"><span class="pre">Iterable[Any]</span></code>).
|
||
The <code class="docutils literal notranslate"><span class="pre">typing</span></code> module will define a special <code class="docutils literal notranslate"><span class="pre">@runtime_checkable</span></code> class decorator
|
||
that provides the same semantics for class and instance checks as for
|
||
<code class="docutils literal notranslate"><span class="pre">collections.abc</span></code> classes, essentially making them “runtime protocols”:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">runtime_checkable</span><span class="p">,</span> <span class="n">Protocol</span>
|
||
|
||
<span class="nd">@runtime_checkable</span>
|
||
<span class="k">class</span> <span class="nc">SupportsClose</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">assert</span> <span class="nb">isinstance</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s1">'some/file'</span><span class="p">),</span> <span class="n">SupportsClose</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that instance checks are not 100% reliable statically, this is why
|
||
this behavior is opt-in, see section on <a class="reference internal" href="#pep-544-rejected">rejected</a>
|
||
ideas for examples.
|
||
The most type checkers can do is to treat <code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">Iterator)</span></code>
|
||
roughly as a simpler way to write
|
||
<code class="docutils literal notranslate"><span class="pre">hasattr(x,</span> <span class="pre">'__iter__')</span> <span class="pre">and</span> <span class="pre">hasattr(x,</span> <span class="pre">'__next__')</span></code>. To minimize
|
||
the risks for this feature, the following rules are applied.</p>
|
||
<p><strong>Definitions</strong>:</p>
|
||
<ul class="simple">
|
||
<li><em>Data, and non-data protocols</em>: A protocol is called non-data protocol
|
||
if it only contains methods as members (for example <code class="docutils literal notranslate"><span class="pre">Sized</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">Iterator</span></code>, etc). A protocol that contains at least one non-method member
|
||
(like <code class="docutils literal notranslate"><span class="pre">x:</span> <span class="pre">int</span></code>) is called a data protocol.</li>
|
||
<li><em>Unsafe overlap</em>: A type <code class="docutils literal notranslate"><span class="pre">X</span></code> is called unsafely overlapping with
|
||
a protocol <code class="docutils literal notranslate"><span class="pre">P</span></code>, if <code class="docutils literal notranslate"><span class="pre">X</span></code> is not a subtype of <code class="docutils literal notranslate"><span class="pre">P</span></code>, but it is a subtype
|
||
of the type erased version of <code class="docutils literal notranslate"><span class="pre">P</span></code> where all members have type <code class="docutils literal notranslate"><span class="pre">Any</span></code>.
|
||
In addition, if at least one element of a union unsafely overlaps with
|
||
a protocol <code class="docutils literal notranslate"><span class="pre">P</span></code>, then the whole union is unsafely overlapping with <code class="docutils literal notranslate"><span class="pre">P</span></code>.</li>
|
||
</ul>
|
||
<p><strong>Specification</strong>:</p>
|
||
<ul class="simple">
|
||
<li>A protocol can be used as a second argument in <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">issubclass()</span></code> only if it is explicitly opt-in by <code class="docutils literal notranslate"><span class="pre">@runtime_checkable</span></code>
|
||
decorator. This requirement exists because protocol checks are not type safe
|
||
in case of dynamically set attributes, and because type checkers can only prove
|
||
that an <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> check is safe only for a given class, not for all its
|
||
subclasses.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> can be used with both data and non-data protocols, while
|
||
<code class="docutils literal notranslate"><span class="pre">issubclass()</span></code> can be used only with non-data protocols. This restriction
|
||
exists because some data attributes can be set on an instance in constructor
|
||
and this information is not always available on the class object.</li>
|
||
<li>Type checkers should reject an <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> or <code class="docutils literal notranslate"><span class="pre">issubclass()</span></code> call, if
|
||
there is an unsafe overlap between the type of the first argument and
|
||
the protocol.</li>
|
||
<li>Type checkers should be able to select a correct element from a union after
|
||
a safe <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> or <code class="docutils literal notranslate"><span class="pre">issubclass()</span></code> call. For narrowing from non-union
|
||
types, type checkers can use their best judgement (this is intentionally
|
||
unspecified, since a precise specification would require intersection types).</li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
<section id="using-protocols-in-python-2-7-3-5">
|
||
<h2><a class="toc-backref" href="#using-protocols-in-python-2-7-3-5" role="doc-backlink">Using Protocols in Python 2.7 - 3.5</a></h2>
|
||
<p>Variable annotation syntax was added in Python 3.6, so that the syntax
|
||
for defining protocol variables proposed in <a class="reference internal" href="#specification">specification</a> section can’t
|
||
be used if support for earlier versions is needed. To define these
|
||
in a manner compatible with older versions of Python one can use properties.
|
||
Properties can be settable and/or abstract if needed:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="nd">@property</span>
|
||
<span class="k">def</span> <span class="nf">c</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="mi">42</span> <span class="c1"># Default value can be provided for property...</span>
|
||
|
||
<span class="nd">@abstractproperty</span>
|
||
<span class="k">def</span> <span class="nf">d</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="c1"># ... or it can be abstract</span>
|
||
<span class="k">return</span> <span class="mi">0</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Also function type comments can be used as per <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> (for example
|
||
to provide compatibility with Python 2). The <code class="docutils literal notranslate"><span class="pre">typing</span></code> module changes
|
||
proposed in this PEP will also be backported to earlier versions via the
|
||
backport currently available on PyPI.</p>
|
||
</section>
|
||
<section id="runtime-implementation-of-protocol-classes">
|
||
<h2><a class="toc-backref" href="#runtime-implementation-of-protocol-classes" role="doc-backlink">Runtime Implementation of Protocol Classes</a></h2>
|
||
<section id="implementation-details">
|
||
<h3><a class="toc-backref" href="#implementation-details" role="doc-backlink">Implementation details</a></h3>
|
||
<p>The runtime implementation could be done in pure Python without any
|
||
effects on the core interpreter and standard library except in the
|
||
<code class="docutils literal notranslate"><span class="pre">typing</span></code> module, and a minor update to <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code>:</p>
|
||
<ul class="simple">
|
||
<li>Define class <code class="docutils literal notranslate"><span class="pre">typing.Protocol</span></code> similar to <code class="docutils literal notranslate"><span class="pre">typing.Generic</span></code>.</li>
|
||
<li>Implement functionality to detect whether a class is
|
||
a protocol or not. Add a class attribute <code class="docutils literal notranslate"><span class="pre">_is_protocol</span> <span class="pre">=</span> <span class="pre">True</span></code>
|
||
if that is the case. Verify that a protocol class only has protocol
|
||
base classes in the MRO (except for object).</li>
|
||
<li>Implement <code class="docutils literal notranslate"><span class="pre">@runtime_checkable</span></code> that allows <code class="docutils literal notranslate"><span class="pre">__subclasshook__()</span></code>
|
||
performing structural instance and subclass checks as in <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code>
|
||
classes.</li>
|
||
<li>All structural subtyping checks will be performed by static type checkers,
|
||
such as <code class="docutils literal notranslate"><span class="pre">mypy</span></code> <a class="reference internal" href="#mypy" id="id9"><span>[mypy]</span></a>. No additional support for protocol validation will
|
||
be provided at runtime.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="changes-in-the-typing-module">
|
||
<h3><a class="toc-backref" href="#changes-in-the-typing-module" role="doc-backlink">Changes in the typing module</a></h3>
|
||
<p>The following classes in <code class="docutils literal notranslate"><span class="pre">typing</span></code> module will be protocols:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">Callable</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Awaitable</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Iterable</span></code>, <code class="docutils literal notranslate"><span class="pre">Iterator</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">AsyncIterable</span></code>, <code class="docutils literal notranslate"><span class="pre">AsyncIterator</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Hashable</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Sized</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Container</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Collection</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">Reversible</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">ContextManager</span></code>, <code class="docutils literal notranslate"><span class="pre">AsyncContextManager</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">SupportsAbs</span></code> (and other <code class="docutils literal notranslate"><span class="pre">Supports*</span></code> classes)</li>
|
||
</ul>
|
||
<p>Most of these classes are small and conceptually simple. It is easy to see
|
||
what are the methods these protocols implement, and immediately recognize
|
||
the corresponding runtime protocol counterpart.
|
||
Practically, few changes will be needed in <code class="docutils literal notranslate"><span class="pre">typing</span></code> since some of these
|
||
classes already behave the necessary way at runtime. Most of these will need
|
||
to be updated only in the corresponding <code class="docutils literal notranslate"><span class="pre">typeshed</span></code> stubs <a class="reference internal" href="#typeshed" id="id10"><span>[typeshed]</span></a>.</p>
|
||
<p>All other concrete generic classes such as <code class="docutils literal notranslate"><span class="pre">List</span></code>, <code class="docutils literal notranslate"><span class="pre">Set</span></code>, <code class="docutils literal notranslate"><span class="pre">IO</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">Deque</span></code>, etc are sufficiently complex that it makes sense to keep
|
||
them non-protocols (i.e. require code to be explicit about them). Also, it is
|
||
too easy to leave some methods unimplemented by accident, and explicitly
|
||
marking the subclass relationship allows type checkers to pinpoint the missing
|
||
implementations.</p>
|
||
</section>
|
||
<section id="introspection">
|
||
<h3><a class="toc-backref" href="#introspection" role="doc-backlink">Introspection</a></h3>
|
||
<p>The existing class introspection machinery (<code class="docutils literal notranslate"><span class="pre">dir</span></code>, <code class="docutils literal notranslate"><span class="pre">__annotations__</span></code> etc)
|
||
can be used with protocols. In addition, all introspection tools implemented
|
||
in the <code class="docutils literal notranslate"><span class="pre">typing</span></code> module will support protocols. Since all attributes need
|
||
to be defined in the class body based on this proposal, protocol classes will
|
||
have even better perspective for introspection than regular classes where
|
||
attributes can be defined implicitly – protocol attributes can’t be
|
||
initialized in ways that are not visible to introspection
|
||
(using <code class="docutils literal notranslate"><span class="pre">setattr()</span></code>, assignment via <code class="docutils literal notranslate"><span class="pre">self</span></code>, etc.). Still, some things like
|
||
types of attributes will not be visible at runtime in Python 3.5 and earlier,
|
||
but this looks like a reasonable limitation.</p>
|
||
<p>There will be only limited support of <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass()</span></code>
|
||
as discussed above (these will <em>always</em> fail with <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> for
|
||
subscripted generic protocols, since a reliable answer could not be given
|
||
at runtime in this case). But together with other introspection tools this
|
||
give a reasonable perspective for runtime type checking tools.</p>
|
||
</section>
|
||
</section>
|
||
<section id="rejected-postponed-ideas">
|
||
<span id="pep-544-rejected"></span><h2><a class="toc-backref" href="#rejected-postponed-ideas" role="doc-backlink">Rejected/Postponed Ideas</a></h2>
|
||
<p>The ideas in this section were previously discussed in <a class="reference internal" href="#several" id="id11"><span>[several]</span></a>
|
||
<a class="reference internal" href="#discussions" id="id12"><span>[discussions]</span></a> <a class="reference internal" href="#elsewhere" id="id13"><span>[elsewhere]</span></a>.</p>
|
||
<section id="make-every-class-a-protocol-by-default">
|
||
<h3><a class="toc-backref" href="#make-every-class-a-protocol-by-default" role="doc-backlink">Make every class a protocol by default</a></h3>
|
||
<p>Some languages such as Go make structural subtyping the only or the primary
|
||
form of subtyping. We could achieve a similar result by making all classes
|
||
protocols by default (or even always). However we believe that it is better
|
||
to require classes to be explicitly marked as protocols, for the following
|
||
reasons:</p>
|
||
<ul class="simple">
|
||
<li>Protocols don’t have some properties of regular classes. In particular,
|
||
<code class="docutils literal notranslate"><span class="pre">isinstance()</span></code>, as defined for normal classes, is based on the nominal
|
||
hierarchy. In order to make everything a protocol by default, and have
|
||
<code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> work would require changing its semantics,
|
||
which won’t happen.</li>
|
||
<li>Protocol classes should generally not have many method implementations,
|
||
as they describe an interface, not an implementation.
|
||
Most classes have many method implementations, making them bad protocol
|
||
classes.</li>
|
||
<li>Experience suggests that many classes are not practical as protocols anyway,
|
||
mainly because their interfaces are too large, complex or
|
||
implementation-oriented (for example, they may include de facto
|
||
private attributes and methods without a <code class="docutils literal notranslate"><span class="pre">__</span></code> prefix).</li>
|
||
<li>Most actually useful protocols in existing Python code seem to be implicit.
|
||
The ABCs in <code class="docutils literal notranslate"><span class="pre">typing</span></code> and <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code> are rather an exception, but
|
||
even they are recent additions to Python and most programmers
|
||
do not use them yet.</li>
|
||
<li>Many built-in functions only accept concrete instances of <code class="docutils literal notranslate"><span class="pre">int</span></code>
|
||
(and subclass instances), and similarly for other built-in classes. Making
|
||
<code class="docutils literal notranslate"><span class="pre">int</span></code> a structural type wouldn’t be safe without major changes to the
|
||
Python runtime, which won’t happen.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="protocols-subclassing-normal-classes">
|
||
<h3><a class="toc-backref" href="#protocols-subclassing-normal-classes" role="doc-backlink">Protocols subclassing normal classes</a></h3>
|
||
<p>The main rationale to prohibit this is to preserve transitivity of subtyping,
|
||
consider this example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Base</span><span class="p">:</span>
|
||
<span class="n">attr</span><span class="p">:</span> <span class="nb">str</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Proto</span><span class="p">(</span><span class="n">Base</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">attr</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="mi">0</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Now, <code class="docutils literal notranslate"><span class="pre">C</span></code> is a subtype of <code class="docutils literal notranslate"><span class="pre">Proto</span></code>, and <code class="docutils literal notranslate"><span class="pre">Proto</span></code> is a subtype of <code class="docutils literal notranslate"><span class="pre">Base</span></code>.
|
||
But <code class="docutils literal notranslate"><span class="pre">C</span></code> cannot be a subtype of <code class="docutils literal notranslate"><span class="pre">Base</span></code> (since the latter is not
|
||
a protocol). This situation would be really weird. In addition, there is
|
||
an ambiguity about whether attributes of <code class="docutils literal notranslate"><span class="pre">Base</span></code> should become protocol
|
||
members of <code class="docutils literal notranslate"><span class="pre">Proto</span></code>.</p>
|
||
</section>
|
||
<section id="support-optional-protocol-members">
|
||
<h3><a class="toc-backref" href="#support-optional-protocol-members" role="doc-backlink">Support optional protocol members</a></h3>
|
||
<p>We can come up with examples where it would be handy to be able to say
|
||
that a method or data attribute does not need to be present in a class
|
||
implementing a protocol, but if it is present, it must conform to a specific
|
||
signature or type. One could use a <code class="docutils literal notranslate"><span class="pre">hasattr()</span></code> check to determine whether
|
||
they can use the attribute on a particular instance.</p>
|
||
<p>Languages such as TypeScript have similar features and
|
||
apparently they are pretty commonly used. The current realistic potential
|
||
use cases for protocols in Python don’t require these. In the interest
|
||
of simplicity, we propose to not support optional methods or attributes.
|
||
We can always revisit this later if there is an actual need.</p>
|
||
</section>
|
||
<section id="allow-only-protocol-methods-and-force-use-of-getters-and-setters">
|
||
<h3><a class="toc-backref" href="#allow-only-protocol-methods-and-force-use-of-getters-and-setters" role="doc-backlink">Allow only protocol methods and force use of getters and setters</a></h3>
|
||
<p>One could argue that protocols typically only define methods, but not
|
||
variables. However, using getters and setters in cases where only a
|
||
simple variable is needed would be quite unpythonic. Moreover, the widespread
|
||
use of properties (that often act as type validators) in large code bases
|
||
is partially due to previous absence of static type checkers for Python,
|
||
the problem that <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> and this PEP are aiming to solve. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># without static types</span>
|
||
|
||
<span class="k">class</span> <span class="nc">MyClass</span><span class="p">:</span>
|
||
<span class="nd">@property</span>
|
||
<span class="k">def</span> <span class="nf">my_attr</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_my_attr</span>
|
||
<span class="nd">@my_attr</span><span class="o">.</span><span class="n">setter</span>
|
||
<span class="k">def</span> <span class="nf">my_attr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">int</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="n">ValidationError</span><span class="p">(</span><span class="s2">"An integer expected for my_attr"</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">_my_attr</span> <span class="o">=</span> <span class="n">value</span>
|
||
|
||
<span class="c1"># with static types</span>
|
||
|
||
<span class="k">class</span> <span class="nc">MyClass</span><span class="p">:</span>
|
||
<span class="n">my_attr</span><span class="p">:</span> <span class="nb">int</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="support-non-protocol-members">
|
||
<h3><a class="toc-backref" href="#support-non-protocol-members" role="doc-backlink">Support non-protocol members</a></h3>
|
||
<p>There was an idea to make some methods “non-protocol” (i.e. not necessary
|
||
to implement, and inherited in explicit subclassing), but it was rejected,
|
||
since this complicates things. For example, consider this situation:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Proto</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="nd">@abstractmethod</span>
|
||
<span class="k">def</span> <span class="nf">first</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
|
||
<span class="k">def</span> <span class="nf">second</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">first</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span>
|
||
|
||
<span class="k">def</span> <span class="nf">fun</span><span class="p">(</span><span class="n">arg</span><span class="p">:</span> <span class="n">Proto</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">arg</span><span class="o">.</span><span class="n">second</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The question is should this be an error? We think most people would expect
|
||
this to be valid. Therefore, to be on the safe side, we need to require both
|
||
methods to be implemented in implicit subclasses. In addition, if one looks
|
||
at definitions in <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code>, there are very few methods that could
|
||
be considered “non-protocol”. Therefore, it was decided to not introduce
|
||
“non-protocol” methods.</p>
|
||
<p>There is only one downside to this: it will require some boilerplate for
|
||
implicit subtypes of “large” protocols. But, this doesn’t apply to “built-in”
|
||
protocols that are all “small” (i.e. have only few abstract methods).
|
||
Also, such style is discouraged for user-defined protocols. It is recommended
|
||
to create compact protocols and combine them.</p>
|
||
</section>
|
||
<section id="make-protocols-interoperable-with-other-approaches">
|
||
<h3><a class="toc-backref" href="#make-protocols-interoperable-with-other-approaches" role="doc-backlink">Make protocols interoperable with other approaches</a></h3>
|
||
<p>The protocols as described here are basically a minimal extension to
|
||
the existing concept of ABCs. We argue that this is the way they should
|
||
be understood, instead of as something that <em>replaces</em> Zope interfaces,
|
||
for example. Attempting such interoperabilities will significantly
|
||
complicate both the concept and the implementation.</p>
|
||
<p>On the other hand, Zope interfaces are conceptually a superset of protocols
|
||
defined here, but using an incompatible syntax to define them,
|
||
because before <a class="pep reference internal" href="../pep-0526/" title="PEP 526 – Syntax for Variable Annotations">PEP 526</a> there was no straightforward way to annotate attributes.
|
||
In the 3.6+ world, <code class="docutils literal notranslate"><span class="pre">zope.interface</span></code> might potentially adopt the <code class="docutils literal notranslate"><span class="pre">Protocol</span></code>
|
||
syntax. In this case, type checkers could be taught to recognize interfaces
|
||
as protocols and make simple structural checks with respect to them.</p>
|
||
</section>
|
||
<section id="use-assignments-to-check-explicitly-that-a-class-implements-a-protocol">
|
||
<h3><a class="toc-backref" href="#use-assignments-to-check-explicitly-that-a-class-implements-a-protocol" role="doc-backlink">Use assignments to check explicitly that a class implements a protocol</a></h3>
|
||
<p>In the Go language the explicit checks for implementation are performed
|
||
via dummy assignments <a class="reference internal" href="#golang" id="id14"><span>[golang]</span></a>. Such a way is also possible with the
|
||
current proposal. Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">A</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="o">...</span>
|
||
|
||
<span class="n">_</span><span class="p">:</span> <span class="n">Sized</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span> <span class="c1"># Error: A.__len__ doesn't conform to 'Sized'</span>
|
||
<span class="c1"># (Incompatible return type 'float')</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This approach moves the check away from
|
||
the class definition and it almost requires a comment as otherwise
|
||
the code probably would not make any sense to an average reader
|
||
– it looks like dead code. Besides, in the simplest form it requires one
|
||
to construct an instance of <code class="docutils literal notranslate"><span class="pre">A</span></code>, which could be problematic if this requires
|
||
accessing or allocating some resources such as files or sockets.
|
||
We could work around the latter by using a cast, for example, but then
|
||
the code would be ugly. Therefore, we discourage the use of this pattern.</p>
|
||
</section>
|
||
<section id="support-isinstance-checks-by-default">
|
||
<h3><a class="toc-backref" href="#support-isinstance-checks-by-default" role="doc-backlink">Support <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> checks by default</a></h3>
|
||
<p>The problem with this is instance checks could be unreliable, except for
|
||
situations where there is a common signature convention such as <code class="docutils literal notranslate"><span class="pre">Iterable</span></code>.
|
||
For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">P</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">common_method_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="k">class</span> <span class="nc">X</span><span class="p">:</span>
|
||
<span class="o"><</span><span class="n">a</span> <span class="n">bunch</span> <span class="n">of</span> <span class="n">methods</span><span class="o">></span>
|
||
<span class="k">def</span> <span class="nf">common_method_name</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span> <span class="c1"># Note different signature</span>
|
||
|
||
<span class="k">def</span> <span class="nf">do_stuff</span><span class="p">(</span><span class="n">o</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">X</span><span class="p">])</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">P</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">o</span><span class="o">.</span><span class="n">common_method_name</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># Results in TypeError not caught</span>
|
||
<span class="c1"># statically if o is an X instance.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Another potentially problematic case is assignment of attributes
|
||
<em>after</em> instantiation:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">P</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span>
|
||
<span class="nb">isinstance</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">P</span><span class="p">)</span> <span class="c1"># False</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">initialize</span><span class="p">()</span>
|
||
<span class="nb">isinstance</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">P</span><span class="p">)</span> <span class="c1"># True</span>
|
||
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">int</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">P</span><span class="p">):</span>
|
||
<span class="c1"># Static type of x is P here.</span>
|
||
<span class="o">...</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># Static type of x is int, but can be other type at runtime...</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
|
||
|
||
<span class="n">f</span><span class="p">(</span><span class="n">C</span><span class="p">())</span> <span class="c1"># ...causing a TypeError.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We argue that requiring an explicit class decorator would be better, since
|
||
one can then attach warnings about problems like this in the documentation.
|
||
The user would be able to evaluate whether the benefits outweigh
|
||
the potential for confusion for each protocol and explicitly opt in – but
|
||
the default behavior would be safer. Finally, it will be easy to make this
|
||
behavior default if necessary, while it might be problematic to make it opt-in
|
||
after being default.</p>
|
||
</section>
|
||
<section id="provide-a-special-intersection-type-construct">
|
||
<h3><a class="toc-backref" href="#provide-a-special-intersection-type-construct" role="doc-backlink">Provide a special intersection type construct</a></h3>
|
||
<p>There was an idea to allow <code class="docutils literal notranslate"><span class="pre">Proto</span> <span class="pre">=</span> <span class="pre">All[Proto1,</span> <span class="pre">Proto2,</span> <span class="pre">...]</span></code> as a shorthand
|
||
for:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Proto</span><span class="p">(</span><span class="n">Proto1</span><span class="p">,</span> <span class="n">Proto2</span><span class="p">,</span> <span class="o">...</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>However, it is not yet clear how popular/useful it will be and implementing
|
||
this in type checkers for non-protocol classes could be difficult. Finally, it
|
||
will be very easy to add this later if needed.</p>
|
||
</section>
|
||
<section id="prohibit-explicit-subclassing-of-protocols-by-non-protocols">
|
||
<h3><a class="toc-backref" href="#prohibit-explicit-subclassing-of-protocols-by-non-protocols" role="doc-backlink">Prohibit explicit subclassing of protocols by non-protocols</a></h3>
|
||
<p>This was rejected for the following reasons:</p>
|
||
<ul class="simple">
|
||
<li>Backward compatibility: People are already using ABCs, including generic
|
||
ABCs from <code class="docutils literal notranslate"><span class="pre">typing</span></code> module. If we prohibit explicit subclassing of these
|
||
ABCs, then quite a lot of code will break.</li>
|
||
<li>Convenience: There are existing protocol-like ABCs (that may be turned
|
||
into protocols) that have many useful “mix-in” (non-abstract) methods.
|
||
For example, in the case of <code class="docutils literal notranslate"><span class="pre">Sequence</span></code> one only needs to implement
|
||
<code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> and <code class="docutils literal notranslate"><span class="pre">__len__</span></code> in an explicit subclass, and one gets
|
||
<code class="docutils literal notranslate"><span class="pre">__iter__</span></code>, <code class="docutils literal notranslate"><span class="pre">__contains__</span></code>, <code class="docutils literal notranslate"><span class="pre">__reversed__</span></code>, <code class="docutils literal notranslate"><span class="pre">index</span></code>, and <code class="docutils literal notranslate"><span class="pre">count</span></code>
|
||
for free.</li>
|
||
<li>Explicit subclassing makes it explicit that a class implements a particular
|
||
protocol, making subtyping relationships easier to see.</li>
|
||
<li>Type checkers can warn about missing protocol members or members with
|
||
incompatible types more easily, without having to use hacks like dummy
|
||
assignments discussed above in this section.</li>
|
||
<li>Explicit subclassing makes it possible to force a class to be considered
|
||
a subtype of a protocol (by using <code class="docutils literal notranslate"><span class="pre">#</span> <span class="pre">type:</span> <span class="pre">ignore</span></code> together with an
|
||
explicit base class) when it is not strictly compatible, such as when
|
||
it has an unsafe override.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="covariant-subtyping-of-mutable-attributes">
|
||
<h3><a class="toc-backref" href="#covariant-subtyping-of-mutable-attributes" role="doc-backlink">Covariant subtyping of mutable attributes</a></h3>
|
||
<p>Rejected because covariant subtyping of mutable attributes is not safe.
|
||
Consider this example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">P</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">float</span>
|
||
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">arg</span><span class="p">:</span> <span class="n">P</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">arg</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mf">0.42</span>
|
||
|
||
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span>
|
||
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span>
|
||
<span class="n">f</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="c1"># Would typecheck if covariant subtyping</span>
|
||
<span class="c1"># of mutable attributes were allowed.</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">x</span> <span class="o">>></span> <span class="mi">1</span> <span class="c1"># But this fails at runtime</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>It was initially proposed to allow this for practical reasons, but it was
|
||
subsequently rejected, since this may mask some hard to spot bugs.</p>
|
||
</section>
|
||
<section id="overriding-inferred-variance-of-protocol-classes">
|
||
<h3><a class="toc-backref" href="#overriding-inferred-variance-of-protocol-classes" role="doc-backlink">Overriding inferred variance of protocol classes</a></h3>
|
||
<p>It was proposed to allow declaring protocols as invariant if they are actually
|
||
covariant or contravariant (as it is possible for nominal classes, see <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>).
|
||
However, it was decided not to do this because of several downsides:</p>
|
||
<ul>
|
||
<li>Declared protocol invariance breaks transitivity of sub-typing. Consider
|
||
this situation:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s1">'T'</span><span class="p">)</span>
|
||
|
||
<span class="k">class</span> <span class="nc">P</span><span class="p">(</span><span class="n">Protocol</span><span class="p">[</span><span class="n">T</span><span class="p">]):</span> <span class="c1"># Protocol is declared as invariant.</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">T</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">class</span> <span class="nc">D</span><span class="p">(</span><span class="n">C</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</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>Now we have that <code class="docutils literal notranslate"><span class="pre">D</span></code> is a subtype of <code class="docutils literal notranslate"><span class="pre">C</span></code>, and <code class="docutils literal notranslate"><span class="pre">C</span></code> is a subtype of
|
||
<code class="docutils literal notranslate"><span class="pre">P[float]</span></code>. But <code class="docutils literal notranslate"><span class="pre">D</span></code> is <em>not</em> a subtype of <code class="docutils literal notranslate"><span class="pre">P[float]</span></code> since <code class="docutils literal notranslate"><span class="pre">D</span></code>
|
||
implements <code class="docutils literal notranslate"><span class="pre">P[int]</span></code>, and <code class="docutils literal notranslate"><span class="pre">P</span></code> is invariant. There is a possibility
|
||
to “cure” this by looking for protocol implementations in MROs but this
|
||
will be too complex in a general case, and this “cure” requires abandoning
|
||
simple idea of purely structural subtyping for protocols.</p>
|
||
</li>
|
||
<li>Subtyping checks will always require type inference for protocols. In the
|
||
above example a user may complain: “Why did you infer <code class="docutils literal notranslate"><span class="pre">P[int]</span></code> for
|
||
my <code class="docutils literal notranslate"><span class="pre">D</span></code>? It implements <code class="docutils literal notranslate"><span class="pre">P[float]</span></code>!”. Normally, inference can be overruled
|
||
by an explicit annotation, but here this will require explicit subclassing,
|
||
defeating the purpose of using protocols.</li>
|
||
<li>Allowing overriding variance will make impossible more detailed error
|
||
messages in type checkers citing particular conflicts in member
|
||
type signatures.</li>
|
||
<li>Finally, explicit is better than implicit in this case. Requiring user to
|
||
declare correct variance will simplify understanding the code and will avoid
|
||
unexpected errors at the point of use.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="support-adapters-and-adaptation">
|
||
<h3><a class="toc-backref" href="#support-adapters-and-adaptation" role="doc-backlink">Support adapters and adaptation</a></h3>
|
||
<p>Adaptation was proposed by <a class="pep reference internal" href="../pep-0246/" title="PEP 246 – Object Adaptation">PEP 246</a> (rejected) and is supported by
|
||
<code class="docutils literal notranslate"><span class="pre">zope.interface</span></code>, see <a class="reference external" href="https://web.archive.org/web/20160802080957/https://docs.zope.org/zope.interface/adapter.html">the Zope documentation on adapter registries</a>.
|
||
Adapters is quite an advanced concept, and <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a> supports unions and
|
||
generic aliases that can be used instead of adapters. This can be illustrated
|
||
with an example of <code class="docutils literal notranslate"><span class="pre">Iterable</span></code> protocol, there is another way of supporting
|
||
iteration by providing <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> and <code class="docutils literal notranslate"><span class="pre">__len__</span></code>. If a function
|
||
supports both this way and the now standard <code class="docutils literal notranslate"><span class="pre">__iter__</span></code> method, then it could
|
||
be annotated by a union type:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">OldIterable</span><span class="p">(</span><span class="n">Sized</span><span class="p">,</span> <span class="n">Protocol</span><span class="p">[</span><span class="n">T</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">item</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">T</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="n">CompatIterable</span> <span class="o">=</span> <span class="n">Union</span><span class="p">[</span><span class="n">Iterable</span><span class="p">[</span><span class="n">T</span><span class="p">],</span> <span class="n">OldIterable</span><span class="p">[</span><span class="n">T</span><span class="p">]]</span>
|
||
|
||
<span class="k">class</span> <span class="nc">A</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Iterator</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">class</span> <span class="nc">B</span><span class="p">:</span>
|
||
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span> <span class="o">...</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">item</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">iterate</span><span class="p">(</span><span class="n">it</span><span class="p">:</span> <span class="n">CompatIterable</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">iterate</span><span class="p">(</span><span class="n">A</span><span class="p">())</span> <span class="c1"># OK</span>
|
||
<span class="n">iterate</span><span class="p">(</span><span class="n">B</span><span class="p">())</span> <span class="c1"># OK</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Since there is a reasonable alternative for such cases with existing tooling,
|
||
it is therefore proposed not to include adaptation in this PEP.</p>
|
||
</section>
|
||
<section id="call-structural-base-types-interfaces">
|
||
<h3><a class="toc-backref" href="#call-structural-base-types-interfaces" role="doc-backlink">Call structural base types “interfaces”</a></h3>
|
||
<p>“Protocol” is a term already widely used in Python to describe duck typing
|
||
contracts such as the iterator protocol (providing <code class="docutils literal notranslate"><span class="pre">__iter__</span></code>
|
||
and <code class="docutils literal notranslate"><span class="pre">__next__</span></code>), and the descriptor protocol (providing <code class="docutils literal notranslate"><span class="pre">__get__</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">__set__</span></code>, and <code class="docutils literal notranslate"><span class="pre">__delete__</span></code>). In addition to this and other reasons given
|
||
in <a class="reference internal" href="#specification">specification</a>, protocols are different from Java interfaces in several
|
||
aspects: protocols don’t require explicit declaration of implementation
|
||
(they are mainly oriented on duck-typing), protocols can have
|
||
default implementations of members and store state.</p>
|
||
</section>
|
||
<section id="make-protocols-special-objects-at-runtime-rather-than-normal-abcs">
|
||
<h3><a class="toc-backref" href="#make-protocols-special-objects-at-runtime-rather-than-normal-abcs" role="doc-backlink">Make protocols special objects at runtime rather than normal ABCs</a></h3>
|
||
<p>Making protocols non-ABCs will make the backwards compatibility problematic
|
||
if possible at all. For example, <code class="docutils literal notranslate"><span class="pre">collections.abc.Iterable</span></code> is already
|
||
an ABC, and lots of existing code use patterns like
|
||
<code class="docutils literal notranslate"><span class="pre">isinstance(obj,</span> <span class="pre">collections.abc.Iterable)</span></code> and similar checks with other
|
||
ABCs (also in a structural manner, i.e., via <code class="docutils literal notranslate"><span class="pre">__subclasshook__</span></code>).
|
||
Disabling this behavior will cause breakages. If we keep this behavior
|
||
for ABCs in <code class="docutils literal notranslate"><span class="pre">collections.abc</span></code> but will not provide a similar runtime
|
||
behavior for protocols in <code class="docutils literal notranslate"><span class="pre">typing</span></code>, then a smooth transition to protocols
|
||
will be not possible. In addition, having two parallel hierarchies may cause
|
||
confusions.</p>
|
||
</section>
|
||
</section>
|
||
<section id="backwards-compatibility">
|
||
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
|
||
<p>This PEP is fully backwards compatible.</p>
|
||
</section>
|
||
<section id="implementation">
|
||
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">mypy</span></code> type checker fully supports protocols (modulo a few
|
||
known bugs). This includes treating all the builtin protocols, such as
|
||
<code class="docutils literal notranslate"><span class="pre">Iterable</span></code> structurally. The runtime implementation of protocols is
|
||
available in <code class="docutils literal notranslate"><span class="pre">typing_extensions</span></code> module on PyPI.</p>
|
||
</section>
|
||
<section id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<div role="list" class="citation-list">
|
||
<div class="citation" id="typing" role="doc-biblioentry">
|
||
<dt class="label" id="typing">[<a href="#id1">typing</a>]</dt>
|
||
<dd><a class="reference external" href="https://docs.python.org/3/library/typing.html">https://docs.python.org/3/library/typing.html</a></div>
|
||
<div class="citation" id="wiki-structural" role="doc-biblioentry">
|
||
<dt class="label" id="wiki-structural">[<a href="#id2">wiki-structural</a>]</dt>
|
||
<dd><a class="reference external" href="https://en.wikipedia.org/wiki/Structural_type_system">https://en.wikipedia.org/wiki/Structural_type_system</a></div>
|
||
<div class="citation" id="zope-interfaces" role="doc-biblioentry">
|
||
<dt class="label" id="zope-interfaces">[<a href="#id3">zope-interfaces</a>]</dt>
|
||
<dd><a class="reference external" href="https://zopeinterface.readthedocs.io/en/latest/">https://zopeinterface.readthedocs.io/en/latest/</a></div>
|
||
<div class="citation" id="abstract-classes" role="doc-biblioentry">
|
||
<dt class="label" id="abstract-classes">[<a href="#id4">abstract-classes</a>]</dt>
|
||
<dd><a class="reference external" href="https://docs.python.org/3/library/abc.html">https://docs.python.org/3/library/abc.html</a></div>
|
||
<div class="citation" id="collections-abc" role="doc-biblioentry">
|
||
<dt class="label" id="collections-abc">[<a href="#id5">collections-abc</a>]</dt>
|
||
<dd><a class="reference external" href="https://docs.python.org/3/library/collections.abc.html">https://docs.python.org/3/library/collections.abc.html</a></div>
|
||
<div class="citation" id="typescript" role="doc-biblioentry">
|
||
<dt class="label" id="typescript">[<a href="#id6">typescript</a>]</dt>
|
||
<dd><a class="reference external" href="https://www.typescriptlang.org/docs/handbook/interfaces.html">https://www.typescriptlang.org/docs/handbook/interfaces.html</a></div>
|
||
<div class="citation" id="golang" role="doc-biblioentry">
|
||
<dt class="label" id="golang">[golang]<em> (<a href='#id7'>1</a>, <a href='#id14'>2</a>) </em></dt>
|
||
<dd><a class="reference external" href="https://golang.org/doc/effective_go.html#interfaces_and_types">https://golang.org/doc/effective_go.html#interfaces_and_types</a></div>
|
||
<div class="citation" id="data-model" role="doc-biblioentry">
|
||
<dt class="label" id="data-model">[<a href="#id8">data-model</a>]</dt>
|
||
<dd><a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#special-method-names">https://docs.python.org/3/reference/datamodel.html#special-method-names</a></div>
|
||
<div class="citation" id="typeshed" role="doc-biblioentry">
|
||
<dt class="label" id="typeshed">[<a href="#id10">typeshed</a>]</dt>
|
||
<dd><a class="reference external" href="https://github.com/python/typeshed/">https://github.com/python/typeshed/</a></div>
|
||
<div class="citation" id="mypy" role="doc-biblioentry">
|
||
<dt class="label" id="mypy">[<a href="#id9">mypy</a>]</dt>
|
||
<dd><a class="reference external" href="http://github.com/python/mypy/">http://github.com/python/mypy/</a></div>
|
||
<div class="citation" id="several" role="doc-biblioentry">
|
||
<dt class="label" id="several">[<a href="#id11">several</a>]</dt>
|
||
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2015-September/thread.html#35859">https://mail.python.org/pipermail/python-ideas/2015-September/thread.html#35859</a></div>
|
||
<div class="citation" id="discussions" role="doc-biblioentry">
|
||
<dt class="label" id="discussions">[<a href="#id12">discussions</a>]</dt>
|
||
<dd><a class="reference external" href="https://github.com/python/typing/issues/11">https://github.com/python/typing/issues/11</a></div>
|
||
<div class="citation" id="elsewhere" role="doc-biblioentry">
|
||
<dt class="label" id="elsewhere">[<a href="#id13">elsewhere</a>]</dt>
|
||
<dd><a class="reference external" href="https://github.com/python/peps/pull/224">https://github.com/python/peps/pull/224</a></div>
|
||
</div>
|
||
</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-0544.rst">https://github.com/python/peps/blob/main/peps/pep-0544.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0544.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><ul>
|
||
<li><a class="reference internal" href="#nominal-vs-structural-subtyping">Nominal vs structural subtyping</a></li>
|
||
<li><a class="reference internal" href="#non-goals">Non-goals</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#existing-approaches-to-structural-subtyping">Existing Approaches to Structural Subtyping</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#terminology">Terminology</a></li>
|
||
<li><a class="reference internal" href="#defining-a-protocol">Defining a protocol</a></li>
|
||
<li><a class="reference internal" href="#protocol-members">Protocol members</a></li>
|
||
<li><a class="reference internal" href="#explicitly-declaring-implementation">Explicitly declaring implementation</a></li>
|
||
<li><a class="reference internal" href="#merging-and-extending-protocols">Merging and extending protocols</a></li>
|
||
<li><a class="reference internal" href="#generic-protocols">Generic protocols</a></li>
|
||
<li><a class="reference internal" href="#recursive-protocols">Recursive protocols</a></li>
|
||
<li><a class="reference internal" href="#self-types-in-protocols">Self-types in protocols</a></li>
|
||
<li><a class="reference internal" href="#callback-protocols">Callback protocols</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#using-protocols">Using Protocols</a><ul>
|
||
<li><a class="reference internal" href="#subtyping-relationships-with-other-types">Subtyping relationships with other types</a></li>
|
||
<li><a class="reference internal" href="#unions-and-intersections-of-protocols">Unions and intersections of protocols</a></li>
|
||
<li><a class="reference internal" href="#type-and-class-objects-vs-protocols"><code class="docutils literal notranslate"><span class="pre">Type[]</span></code> and class objects vs protocols</a></li>
|
||
<li><a class="reference internal" href="#newtype-and-type-aliases"><code class="docutils literal notranslate"><span class="pre">NewType()</span></code> and type aliases</a></li>
|
||
<li><a class="reference internal" href="#modules-as-implementations-of-protocols">Modules as implementations of protocols</a></li>
|
||
<li><a class="reference internal" href="#runtime-checkable-decorator-and-narrowing-types-by-isinstance"><code class="docutils literal notranslate"><span class="pre">@runtime_checkable</span></code> decorator and narrowing types by <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#using-protocols-in-python-2-7-3-5">Using Protocols in Python 2.7 - 3.5</a></li>
|
||
<li><a class="reference internal" href="#runtime-implementation-of-protocol-classes">Runtime Implementation of Protocol Classes</a><ul>
|
||
<li><a class="reference internal" href="#implementation-details">Implementation details</a></li>
|
||
<li><a class="reference internal" href="#changes-in-the-typing-module">Changes in the typing module</a></li>
|
||
<li><a class="reference internal" href="#introspection">Introspection</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-postponed-ideas">Rejected/Postponed Ideas</a><ul>
|
||
<li><a class="reference internal" href="#make-every-class-a-protocol-by-default">Make every class a protocol by default</a></li>
|
||
<li><a class="reference internal" href="#protocols-subclassing-normal-classes">Protocols subclassing normal classes</a></li>
|
||
<li><a class="reference internal" href="#support-optional-protocol-members">Support optional protocol members</a></li>
|
||
<li><a class="reference internal" href="#allow-only-protocol-methods-and-force-use-of-getters-and-setters">Allow only protocol methods and force use of getters and setters</a></li>
|
||
<li><a class="reference internal" href="#support-non-protocol-members">Support non-protocol members</a></li>
|
||
<li><a class="reference internal" href="#make-protocols-interoperable-with-other-approaches">Make protocols interoperable with other approaches</a></li>
|
||
<li><a class="reference internal" href="#use-assignments-to-check-explicitly-that-a-class-implements-a-protocol">Use assignments to check explicitly that a class implements a protocol</a></li>
|
||
<li><a class="reference internal" href="#support-isinstance-checks-by-default">Support <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> checks by default</a></li>
|
||
<li><a class="reference internal" href="#provide-a-special-intersection-type-construct">Provide a special intersection type construct</a></li>
|
||
<li><a class="reference internal" href="#prohibit-explicit-subclassing-of-protocols-by-non-protocols">Prohibit explicit subclassing of protocols by non-protocols</a></li>
|
||
<li><a class="reference internal" href="#covariant-subtyping-of-mutable-attributes">Covariant subtyping of mutable attributes</a></li>
|
||
<li><a class="reference internal" href="#overriding-inferred-variance-of-protocol-classes">Overriding inferred variance of protocol classes</a></li>
|
||
<li><a class="reference internal" href="#support-adapters-and-adaptation">Support adapters and adaptation</a></li>
|
||
<li><a class="reference internal" href="#call-structural-base-types-interfaces">Call structural base types “interfaces”</a></li>
|
||
<li><a class="reference internal" href="#make-protocols-special-objects-at-runtime-rather-than-normal-abcs">Make protocols special objects at runtime rather than normal ABCs</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</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-0544.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> |