peps/pep-0645/index.html

250 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 645 Allow writing optional types as x? | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0645/">
<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 645 Allow writing optional types as x? | peps.python.org'>
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0645/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="Python Enhancement Proposals (PEPs)">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 645</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 645 Allow writing optional types as <code class="docutils literal notranslate"><span class="pre">x?</span></code></h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Maggie Moss &lt;maggiebmoss&#32;&#97;t&#32;gmail.com&gt;</dd>
<dt class="field-even">Sponsor<span class="colon">:</span></dt>
<dd class="field-even">Guido van Rossum &lt;guido&#32;&#97;t&#32;python.org&gt;</dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Removed from consideration by sponsor or authors">Withdrawn</abbr></dd>
<dt class="field-even">Type<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-odd">Created<span class="colon">:</span></dt>
<dd class="field-odd">25-Aug-2020</dd>
<dt class="field-even">Resolution<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/message/E75SPV6DDHLEEFSA5MBN5HUOQWDMUQJ2/">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="#pep-withdrawal">PEP Withdrawal</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>This PEP proposes adding a <code class="docutils literal notranslate"><span class="pre">?</span></code> operator for types to allow writing <code class="docutils literal notranslate"><span class="pre">int?</span></code> in place of <code class="docutils literal notranslate"><span class="pre">Optional[int]</span></code>.</p>
</section>
<section id="pep-withdrawal">
<h2><a class="toc-backref" href="#pep-withdrawal" role="doc-backlink">PEP Withdrawal</a></h2>
<p>The notation <code class="docutils literal notranslate"><span class="pre">T|None</span></code> introduced by <a class="pep reference internal" href="../pep-0604/" title="PEP 604 Allow writing union types as X | Y">PEP 604</a> to write <code class="docutils literal notranslate"><span class="pre">Optional[T]</span></code> is a
fine alternative to <code class="docutils literal notranslate"><span class="pre">T?</span></code> and does not require new syntax.</p>
<p>Using <code class="docutils literal notranslate"><span class="pre">T?</span></code> to mean <code class="docutils literal notranslate"><span class="pre">T|None</span></code> is also inconsistent with TypeScript
where it roughly means <code class="docutils literal notranslate"><span class="pre">NotRequired[T]</span></code>.
Such inconsistency would likely confuse folks coming from TypeScript to Python.</p>
<p>The above represents the consensus of
<a class="reference external" href="https://mail.python.org/archives/list/typing-sig&#64;python.org/">typing-sig</a>
and the sponsor of this PEP.</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>Types have become a valuable and powerful part of the Python language. However, many type annotations are verbose and add
considerable friction to using type annotations. By improving the typing syntax, adding types to Python code becomes simpler
and improves the development experience for Python users.</p>
<p>In a similar vein, a PEP to introduce short hand syntax for <a class="pep reference internal" href="../pep-0604/" title="PEP 604 Allow writing union types as X | Y">Union types</a> has
been approved and implemented.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>Types in Python can be quite verbose, this can be a hindrance when working towards type adoption. Making types more ergonomic,
as was done with the Union type in <a class="pep reference internal" href="../pep-0604/" title="PEP 604 Allow writing union types as X | Y">PEP 604</a> (e.g., int | str), would reduce the effort needed to add types to new and existing Python code.
The Optional annotation is used frequently in both partially and fully typed Python code bases. In a small sampling of <a class="reference external" href="https://gist.github.com/MaggieMoss/fd8dfe002b2702fae243dbf81a62624e">5 well-typed open
source projects, on average 7% of annotations</a> included at least one optional type. This indicates
that updating the syntax has the potential to make types more concise, reduce code length and improve readability.</p>
<p>Simplifying the syntax for optionals has been <a class="reference external" href="https://github.com/python/typing/issues/429">discussed previously</a> within the typing community.
The consensus during these conversations has been that <code class="docutils literal notranslate"><span class="pre">?</span></code> is the preferred operator. There is no native support for unary <code class="docutils literal notranslate"><span class="pre">?</span></code> in Python and this will
need to be added to the runtime.</p>
<p>Adding the ? sigil to the Python grammar has been proposed previously in <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a>, which is currently in a deferred state.
<a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a> proposes a:</p>
<blockquote>
<div><ul class="simple">
<li>“None coalescing” binary operator <code class="docutils literal notranslate"><span class="pre">??</span></code></li>
<li>“None-aware attribute access” operator <code class="docutils literal notranslate"><span class="pre">?.</span></code> (“maybe dot”)</li>
<li>“None-aware indexing” operator <code class="docutils literal notranslate"><span class="pre">?[]</span></code> (“maybe subscript”)</li>
</ul>
</div></blockquote>
<p>Should <a class="pep reference internal" href="../pep-0505/" title="PEP 505 None-aware operators">PEP 505</a> be approved in the future, it would not interfere with the typing specific <code class="docutils literal notranslate"><span class="pre">?</span></code> proposed in this PEP. As well,
since all uses of the <code class="docutils literal notranslate"><span class="pre">?</span></code> would be conceptually related, it would not be confusing in terms of learning Python or a hindrance to quick visual comprehension.</p>
<p>The proposed syntax, with the postfix operator, mimics the optional syntax found in other typed languages, like C#, TypeScript and Swift.
The widespread adoption and popularity of these languages means that Python developers are likely already familiar with this syntax.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>// Optional in Swift
var example: String?
// Optional in C#
string? example;
</pre></div>
</div>
<p>Adding this syntax would also follow the often used pattern of using builtin types as annotations. For example, <code class="docutils literal notranslate"><span class="pre">list</span></code>, <code class="docutils literal notranslate"><span class="pre">dict</span></code> and <code class="docutils literal notranslate"><span class="pre">None</span></code>. This would allow more annotations to be
added to Python code without importing from <code class="docutils literal notranslate"><span class="pre">typing</span></code>.</p>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>The new optional syntax should be accepted for function, variable, attribute and parameter annotations.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># instead of
# def foo(x: Optional[int], y: Optional[str], z: Optional[list[int]): ...
def foo(x: int?, y: str?, x: list[int]?): ...
# def bar(x: list[typing.Optional[int]]): ...
def bar(x: list[int?]): ...
</pre></div>
</div>
<p>The new optional syntax should be equivalent to the existing typing.Optional syntax</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>typing.Optional[int] == int?
</pre></div>
</div>
<p>The new optional syntax should have the same identity as the existing typing.Optional syntax.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>typing.Optional[int] is int?
</pre></div>
</div>
<p>It should also be equivalent to a Union with None.</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># old syntax
int? == typing.Union[int, None]
# new syntax
int? == int | None
</pre></div>
</div>
<p>Since the new Union syntax specified in <a class="pep reference internal" href="../pep-0604/" title="PEP 604 Allow writing union types as X | Y">PEP 604</a> is supported in <code class="docutils literal notranslate"><span class="pre">isinstance</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass</span></code>, the new optional syntax should be supported in both <code class="docutils literal notranslate"><span class="pre">isinstance</span></code> and <code class="docutils literal notranslate"><span class="pre">issubclass</span></code>,</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>isinstance(1, int?) # true
issubclass(Child, Super?) # true
</pre></div>
</div>
<p>A new dunder method will need to be implemented to allow the <code class="docutils literal notranslate"><span class="pre">?</span></code> operator to be overloaded for other functionality.</p>
</section>
<section id="backwards-compatibility">
<h2><a class="toc-backref" href="#backwards-compatibility" role="doc-backlink">Backwards Compatibility</a></h2>
<p><code class="docutils literal notranslate"><span class="pre">?</span></code> is currently unused in Python syntax, therefore this PEP is fully backwards compatible.</p>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>A reference implementation can be found <a class="reference external" href="https://github.com/python/cpython/compare/main...MaggieMoss:new-optional-syntax-postfix">here</a>.</p>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<p>Discussed alternatives were</p>
<ul class="simple">
<li>The <code class="docutils literal notranslate"><span class="pre">~</span></code> operator was considered in place of <code class="docutils literal notranslate"><span class="pre">?</span></code>.</li>
<li>A prefix operator (<code class="docutils literal notranslate"><span class="pre">?int</span></code>).</li>
</ul>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0645.rst">https://github.com/python/peps/blob/main/peps/pep-0645.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0645.rst">2023-09-09 17:39:29 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="#pep-withdrawal">PEP Withdrawal</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#backwards-compatibility">Backwards Compatibility</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</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-0645.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>