502 lines
41 KiB
HTML
502 lines
41 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 661 – Sentinel Values | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0661/">
|
||
<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 661 – Sentinel Values | peps.python.org'>
|
||
<meta property="og:description" content="Unique placeholder values, commonly known as “sentinel values”, are common in programming. They have many uses, such as for:">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0661/">
|
||
<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="Unique placeholder values, commonly known as “sentinel values”, are common in programming. They have many uses, such as for:">
|
||
<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 661</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 661 – Sentinel Values</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Tal Einat <tal at python.org></dd>
|
||
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-661-sentinel-values/9126">Discourse thread</a></dd>
|
||
<dt class="field-odd">Status<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Proposal under active discussion and revision">Draft</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">06-Jun-2021</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">06-Jun-2021</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="#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="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#use-notgiven-object">Use <code class="docutils literal notranslate"><span class="pre">NotGiven</span> <span class="pre">=</span> <span class="pre">object()</span></code></a></li>
|
||
<li><a class="reference internal" href="#add-a-single-new-sentinel-value-such-as-missing-or-sentinel">Add a single new sentinel value, such as <code class="docutils literal notranslate"><span class="pre">MISSING</span></code> or <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code></a></li>
|
||
<li><a class="reference internal" href="#use-the-existing-ellipsis-sentinel-value">Use the existing <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> sentinel value</a></li>
|
||
<li><a class="reference internal" href="#use-a-single-valued-enum">Use a single-valued enum</a></li>
|
||
<li><a class="reference internal" href="#a-sentinel-class-decorator">A sentinel class decorator</a></li>
|
||
<li><a class="reference internal" href="#using-class-objects">Using class objects</a></li>
|
||
<li><a class="reference internal" href="#define-a-recommended-standard-idiom-without-supplying-an-implementation">Define a recommended “standard” idiom, without supplying an implementation</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#additional-notes">Additional Notes</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>
|
||
<p>TL;DR: See the <a class="reference internal" href="#specification">Specification</a> and <a class="reference internal" href="#reference-implementation">Reference Implementation</a>.</p>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>Unique placeholder values, commonly known as “sentinel values”, are common in
|
||
programming. They have many uses, such as for:</p>
|
||
<ul>
|
||
<li>Default values for function arguments, for when a value was not given:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Return values from functions when something is not found or unavailable:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="s2">"abc"</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">"d"</span><span class="p">)</span>
|
||
<span class="go">-1</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Missing data, such as NULL in relational databases or “N/A” (“not
|
||
available”) in spreadsheets</li>
|
||
</ul>
|
||
<p>Python has the special value <code class="docutils literal notranslate"><span class="pre">None</span></code>, which is intended to be used as such
|
||
a sentinel value in most cases. However, sometimes an alternative sentinel
|
||
value is needed, usually when it needs to be distinct from <code class="docutils literal notranslate"><span class="pre">None</span></code>. These
|
||
cases are common enough that several idioms for implementing such sentinels
|
||
have arisen over the years, but uncommon enough that there hasn’t been a
|
||
clear need for standardization. However, the common implementations,
|
||
including some in the stdlib, suffer from several significant drawbacks.</p>
|
||
<p>This PEP proposes adding a utility for defining sentinel values, to be used
|
||
in the stdlib and made publicly available as part of the stdlib.</p>
|
||
<p>Note: Changing all existing sentinels in the stdlib to be implemented this
|
||
way is not deemed necessary, and whether to do so is left to the discretion
|
||
of the maintainers.</p>
|
||
</section>
|
||
<section id="motivation">
|
||
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
|
||
<p>In May 2021, a question was brought up on the python-dev mailing list
|
||
<a class="footnote-reference brackets" href="#id14" id="id1">[1]</a> about how to better implement a sentinel value for
|
||
<code class="docutils literal notranslate"><span class="pre">traceback.print_exception</span></code>. The existing implementation used the
|
||
following common idiom:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_sentinel</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>However, this object has an uninformative and overly verbose repr, causing the
|
||
function’s signature to be overly long and hard to read:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">help</span><span class="p">(</span><span class="n">traceback</span><span class="o">.</span><span class="n">print_exception</span><span class="p">)</span>
|
||
<span class="go">Help on function print_exception in module traceback:</span>
|
||
|
||
<span class="go">print_exception(exc, /, value=<object object at</span>
|
||
<span class="go">0x000002825DF09650>, tb=<object object at 0x000002825DF09650>,</span>
|
||
<span class="go">limit=None, file=None, chain=True)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Additionally, two other drawbacks of many existing sentinels were brought up
|
||
in the discussion:</p>
|
||
<ol class="arabic simple">
|
||
<li>Not having a distinct type, hence it being impossible to define clear
|
||
type signatures for functions with sentinels as default values</li>
|
||
<li>Incorrect behavior after being copied or unpickled, due to a separate
|
||
instance being created and thus comparisons using <code class="docutils literal notranslate"><span class="pre">is</span></code> failing</li>
|
||
</ol>
|
||
<p>In the ensuing discussion, Victor Stinner supplied a list of currently used
|
||
sentinel values in the Python standard library <a class="footnote-reference brackets" href="#id15" id="id2">[2]</a>. This showed that the
|
||
need for sentinels is fairly common, that there are various implementation
|
||
methods used even within the stdlib, and that many of these suffer from at
|
||
least one of the three above drawbacks.</p>
|
||
<p>The discussion did not lead to any clear consensus on whether a standard
|
||
implementation method is needed or desirable, whether the drawbacks mentioned
|
||
are significant, nor which kind of implementation would be good. The author
|
||
of this PEP created an issue on bugs.python.org <a class="footnote-reference brackets" href="#id16" id="id3">[3]</a> suggesting options for
|
||
improvement, but that focused on only a single problematic aspect of a few
|
||
cases, and failed to gather any support.</p>
|
||
<p>A poll <a class="footnote-reference brackets" href="#id17" id="id4">[4]</a> was created on discuss.python.org to get a clearer sense of
|
||
the community’s opinions. The poll’s results were not conclusive, with 40%
|
||
voting for “The status-quo is fine / there’s no need for consistency in
|
||
this”, but most voters voting for one or more standardized solutions.
|
||
Specifically, 37% of the voters chose “Consistent use of a new, dedicated
|
||
sentinel factory / class / meta-class, also made publicly available in the
|
||
stdlib”.</p>
|
||
<p>With such mixed opinions, this PEP was created to facilitate making a decision
|
||
on the subject.</p>
|
||
<p>While working on this PEP, iterating on various options and implementations
|
||
and continuing discussions, the author has come to the opinion that a simple,
|
||
good implementation available in the standard library would be worth having,
|
||
both for use in the standard library itself and elsewhere.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>The criteria guiding the chosen implementation were:</p>
|
||
<ol class="arabic simple">
|
||
<li>The sentinel objects should behave as expected by a sentinel object: When
|
||
compared using the <code class="docutils literal notranslate"><span class="pre">is</span></code> operator, it should always be considered
|
||
identical to itself but never to any other object.</li>
|
||
<li>Creating a sentinel object should be a simple, straightforward one-liner.</li>
|
||
<li>It should be simple to define as many distinct sentinel values as needed.</li>
|
||
<li>The sentinel objects should have a clear and short repr.</li>
|
||
<li>It should be possible to use clear type signatures for sentinels.</li>
|
||
<li>The sentinel objects should behave correctly after copying and/or
|
||
unpickling.</li>
|
||
<li>Such sentinels should work when using CPython 3.x and PyPy3, and ideally
|
||
also with other implementations of Python.</li>
|
||
<li>As simple and straightforward as possible, in implementation and especially
|
||
in use. Avoid this becoming one more special thing to learn when learning
|
||
Python. It should be easy to find and use when needed, and obvious enough
|
||
when reading code that one would normally not feel a need to look up its
|
||
documentation.</li>
|
||
</ol>
|
||
<p>With so many uses in the Python standard library <a class="footnote-reference brackets" href="#id15" id="id5">[2]</a>, it would be useful to
|
||
have an implementation in the standard library, since the stdlib cannot use
|
||
implementations of sentinel objects available elsewhere (such as the
|
||
<code class="docutils literal notranslate"><span class="pre">sentinels</span></code> <a class="footnote-reference brackets" href="#id18" id="id6">[5]</a> or <code class="docutils literal notranslate"><span class="pre">sentinel</span></code> <a class="footnote-reference brackets" href="#id19" id="id7">[6]</a> PyPI packages).</p>
|
||
<p>After researching existing idioms and implementations, and going through many
|
||
different possible implementations, an implementation was written which meets
|
||
all of these criteria (see <a class="reference internal" href="#reference-implementation">Reference Implementation</a>).</p>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<p>A new <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code> class will be added to a new <code class="docutils literal notranslate"><span class="pre">sentinels</span></code> module.
|
||
Its initializer will accept a single required argument, the name of the
|
||
sentinel object, and two optional arguments: the repr of the object, and the
|
||
name of its module:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">sentinels</span> <span class="kn">import</span> <span class="n">Sentinel</span>
|
||
<span class="gp">>>> </span><span class="n">NotGiven</span> <span class="o">=</span> <span class="n">Sentinel</span><span class="p">(</span><span class="s1">'NotGiven'</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">NotGiven</span>
|
||
<span class="go"><NotGiven></span>
|
||
<span class="gp">>>> </span><span class="n">MISSING</span> <span class="o">=</span> <span class="n">Sentinel</span><span class="p">(</span><span class="s1">'MISSING'</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="s1">'mymodule.MISSING'</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">MISSING</span>
|
||
<span class="go">mymodule.MISSING</span>
|
||
<span class="gp">>>> </span><span class="n">MEGA</span> <span class="o">=</span> <span class="n">Sentinel</span><span class="p">(</span><span class="s1">'MEGA'</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="s1">'<MEGA>'</span><span class="p">,</span> <span class="n">module_name</span><span class="o">=</span><span class="s1">'mymodule'</span><span class="p">)</span>
|
||
<span class="go"><MEGA></span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Checking if a value is such a sentinel <em>should</em> be done using the <code class="docutils literal notranslate"><span class="pre">is</span></code>
|
||
operator, as is recommended for <code class="docutils literal notranslate"><span class="pre">None</span></code>. Equality checks using <code class="docutils literal notranslate"><span class="pre">==</span></code> will
|
||
also work as expected, returning <code class="docutils literal notranslate"><span class="pre">True</span></code> only when the object is compared
|
||
with itself. Identity checks such as <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">value</span> <span class="pre">is</span> <span class="pre">MISSING:</span></code> should usually
|
||
be used rather than boolean checks such as <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">value:</span></code> or <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">not</span> <span class="pre">value:</span></code>.
|
||
Sentinel instances are truthy by default, unlike <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
|
||
<p>The names of sentinels are unique within each module. When calling
|
||
<code class="docutils literal notranslate"><span class="pre">Sentinel()</span></code> in a module where a sentinel with that name was already
|
||
defined, the existing sentinel with that name will be returned. Sentinels
|
||
with the same name in different modules will be distinct from each other.</p>
|
||
<p>Creating a copy of a sentinel object, such as by using <code class="docutils literal notranslate"><span class="pre">copy.copy()</span></code> or by
|
||
pickling and unpickling, will return the same object.</p>
|
||
<p>Type annotations for sentinel values should use <code class="docutils literal notranslate"><span class="pre">Literal[<sentinel_object>]</span></code>.
|
||
For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="n">Literal</span><span class="p">[</span><span class="n">MISSING</span><span class="p">]</span> <span class="o">=</span> <span class="n">MISSING</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>The <code class="docutils literal notranslate"><span class="pre">module_name</span></code> optional argument should normally not need to be supplied,
|
||
as <code class="docutils literal notranslate"><span class="pre">Sentinel()</span></code> will usually be able to recognize the module in which it was
|
||
called. <code class="docutils literal notranslate"><span class="pre">module_name</span></code> should be supplied only in unusual cases when this
|
||
automatic recognition does not work as intended, such as perhaps when using
|
||
Jython or IronPython. This parallels the designs of <code class="docutils literal notranslate"><span class="pre">Enum</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">namedtuple</span></code>. For more details, see <a class="pep reference internal" href="../pep-0435/" title="PEP 435 – Adding an Enum type to the Python standard library">PEP 435</a>.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code> class may be sub-classed. Instances of each sub-class will
|
||
be unique, even if using the same name and module. This allows for
|
||
customizing the behavior of sentinels, such as controlling their truthiness.</p>
|
||
</section>
|
||
<section id="reference-implementation">
|
||
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
|
||
<p>The reference implementation is found in a dedicated GitHub repo <a class="footnote-reference brackets" href="#id20" id="id8">[7]</a>. A
|
||
simplified version follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_registry</span> <span class="o">=</span> <span class="p">{}</span>
|
||
|
||
<span class="k">class</span> <span class="nc">Sentinel</span><span class="p">:</span>
|
||
<span class="w"> </span><span class="sd">"""Unique sentinel values."""</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="nb">repr</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">module_name</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
||
<span class="n">name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
|
||
<span class="nb">repr</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="nb">repr</span><span class="p">)</span> <span class="k">if</span> <span class="nb">repr</span> <span class="k">else</span> <span class="sa">f</span><span class="s1">'<</span><span class="si">{</span><span class="n">name</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s1">>'</span>
|
||
<span class="k">if</span> <span class="n">module_name</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">module_name</span> <span class="o">=</span> \
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">_getframe</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">f_globals</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'__name__'</span><span class="p">,</span> <span class="s1">'__main__'</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="p">(</span><span class="ne">AttributeError</span><span class="p">,</span> <span class="ne">ValueError</span><span class="p">):</span>
|
||
<span class="n">module_name</span> <span class="o">=</span> <span class="vm">__name__</span>
|
||
|
||
<span class="n">registry_key</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'</span><span class="si">{</span><span class="n">module_name</span><span class="si">}</span><span class="s1">-</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1">'</span>
|
||
|
||
<span class="n">sentinel</span> <span class="o">=</span> <span class="n">_registry</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">registry_key</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">sentinel</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">sentinel</span>
|
||
|
||
<span class="n">sentinel</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
|
||
<span class="n">sentinel</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span>
|
||
<span class="n">sentinel</span><span class="o">.</span><span class="n">_repr</span> <span class="o">=</span> <span class="nb">repr</span>
|
||
<span class="n">sentinel</span><span class="o">.</span><span class="n">_module_name</span> <span class="o">=</span> <span class="n">module_name</span>
|
||
|
||
<span class="k">return</span> <span class="n">_registry</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">registry_key</span><span class="p">,</span> <span class="n">sentinel</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__repr__</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">_repr</span>
|
||
|
||
<span class="k">def</span> <span class="nf">__reduce__</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="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">,</span>
|
||
<span class="p">(</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span><span class="p">,</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">_repr</span><span class="p">,</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">_module_name</span><span class="p">,</span>
|
||
<span class="p">),</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
|
||
<section id="use-notgiven-object">
|
||
<h3><a class="toc-backref" href="#use-notgiven-object" role="doc-backlink">Use <code class="docutils literal notranslate"><span class="pre">NotGiven</span> <span class="pre">=</span> <span class="pre">object()</span></code></a></h3>
|
||
<p>This suffers from all of the drawbacks mentioned in the <a class="reference internal" href="#rationale">Rationale</a> section.</p>
|
||
</section>
|
||
<section id="add-a-single-new-sentinel-value-such-as-missing-or-sentinel">
|
||
<h3><a class="toc-backref" href="#add-a-single-new-sentinel-value-such-as-missing-or-sentinel" role="doc-backlink">Add a single new sentinel value, such as <code class="docutils literal notranslate"><span class="pre">MISSING</span></code> or <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code></a></h3>
|
||
<p>Since such a value could be used for various things in various places, one
|
||
could not always be confident that it would never be a valid value in some use
|
||
cases. On the other hand, a dedicated and distinct sentinel value can be used
|
||
with confidence without needing to consider potential edge-cases.</p>
|
||
<p>Additionally, it is useful to be able to provide a meaningful name and repr
|
||
for a sentinel value, specific to the context where it is used.</p>
|
||
<p>Finally, this was a very unpopular option in the poll <a class="footnote-reference brackets" href="#id17" id="id9">[4]</a>, with only 12%
|
||
of the votes voting for it.</p>
|
||
</section>
|
||
<section id="use-the-existing-ellipsis-sentinel-value">
|
||
<h3><a class="toc-backref" href="#use-the-existing-ellipsis-sentinel-value" role="doc-backlink">Use the existing <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> sentinel value</a></h3>
|
||
<p>This is not the original intended use of Ellipsis, though it has become
|
||
increasingly common to use it to define empty class or function blocks instead
|
||
of using <code class="docutils literal notranslate"><span class="pre">pass</span></code>.</p>
|
||
<p>Also, similar to a potential new single sentinel value, <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> can’t be
|
||
as confidently used in all cases, unlike a dedicated, distinct value.</p>
|
||
</section>
|
||
<section id="use-a-single-valued-enum">
|
||
<h3><a class="toc-backref" href="#use-a-single-valued-enum" role="doc-backlink">Use a single-valued enum</a></h3>
|
||
<p>The suggested idiom is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">NotGivenType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
|
||
<span class="n">NotGiven</span> <span class="o">=</span> <span class="s1">'NotGiven'</span>
|
||
<span class="n">NotGiven</span> <span class="o">=</span> <span class="n">NotGivenType</span><span class="o">.</span><span class="n">NotGiven</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Besides the excessive repetition, the repr is overly long:
|
||
<code class="docutils literal notranslate"><span class="pre"><NotGivenType.NotGiven:</span> <span class="pre">'NotGiven'></span></code>. A shorter repr can be defined, at
|
||
the expense of a bit more code and yet more repetition.</p>
|
||
<p>Finally, this option was the least popular among the nine options in the
|
||
poll <a class="footnote-reference brackets" href="#id17" id="id10">[4]</a>, being the only option to receive no votes.</p>
|
||
</section>
|
||
<section id="a-sentinel-class-decorator">
|
||
<h3><a class="toc-backref" href="#a-sentinel-class-decorator" role="doc-backlink">A sentinel class decorator</a></h3>
|
||
<p>The suggested idiom is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@sentinel</span><span class="p">(</span><span class="nb">repr</span><span class="o">=</span><span class="s1">'<NotGiven>'</span><span class="p">)</span>
|
||
<span class="k">class</span> <span class="nc">NotGivenType</span><span class="p">:</span> <span class="k">pass</span>
|
||
<span class="n">NotGiven</span> <span class="o">=</span> <span class="n">NotGivenType</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>While this allows for a very simple and clear implementation of the decorator,
|
||
the idiom is too verbose, repetitive, and difficult to remember.</p>
|
||
</section>
|
||
<section id="using-class-objects">
|
||
<h3><a class="toc-backref" href="#using-class-objects" role="doc-backlink">Using class objects</a></h3>
|
||
<p>Since classes are inherently singletons, using a class as a sentinel value
|
||
makes sense and allows for a simple implementation.</p>
|
||
<p>The simplest version of this is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">NotGiven</span><span class="p">:</span> <span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>To have a clear repr, one would need to use a meta-class:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">NotGiven</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">SentinelMeta</span><span class="p">):</span> <span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>… or a class decorator:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@Sentinel</span>
|
||
<span class="k">class</span> <span class="nc">NotGiven</span><span class="p">:</span> <span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Using classes this way is unusual and could be confusing. The intention of
|
||
code would be hard to understand without comments. It would also cause
|
||
such sentinels to have some unexpected and undesirable behavior, such as
|
||
being callable.</p>
|
||
</section>
|
||
<section id="define-a-recommended-standard-idiom-without-supplying-an-implementation">
|
||
<h3><a class="toc-backref" href="#define-a-recommended-standard-idiom-without-supplying-an-implementation" role="doc-backlink">Define a recommended “standard” idiom, without supplying an implementation</a></h3>
|
||
<p>Most common existing idioms have significant drawbacks. So far, no idiom
|
||
has been found that is clear and concise while avoiding these drawbacks.</p>
|
||
<p>Also, in the poll <a class="footnote-reference brackets" href="#id17" id="id11">[4]</a> on this subject, the options for recommending an
|
||
idiom were unpopular, with the highest-voted option being voted for by only
|
||
25% of the voters.</p>
|
||
</section>
|
||
</section>
|
||
<section id="additional-notes">
|
||
<h2><a class="toc-backref" href="#additional-notes" role="doc-backlink">Additional Notes</a></h2>
|
||
<ul>
|
||
<li>This PEP and the initial implementation are drafted in a dedicated GitHub
|
||
repo <a class="footnote-reference brackets" href="#id20" id="id12">[7]</a>.</li>
|
||
<li>For sentinels defined in a class scope, to avoid potential name clashes,
|
||
one should use the fully-qualified name of the variable in the module. Only
|
||
the part of the name after the last period will be used for the default
|
||
repr. For example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">class</span> <span class="nc">MyClass</span><span class="p">:</span>
|
||
<span class="gp">... </span> <span class="n">NotGiven</span> <span class="o">=</span> <span class="n">sentinel</span><span class="p">(</span><span class="s1">'MyClass.NotGiven'</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="n">MyClass</span><span class="o">.</span><span class="n">NotGiven</span>
|
||
<span class="go"><NotGiven></span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>One should be careful when creating sentinels in a function or method, since
|
||
sentinels with the same name created by code in the same module will be
|
||
identical. If distinct sentinel objects are needed, make sure to use
|
||
distinct names.</li>
|
||
<li>There was a discussion on the typing-sig mailing list <a class="footnote-reference brackets" href="#id21" id="id13">[8]</a> about the typing
|
||
for these sentinels, where different options were discussed.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id14" role="doc-footnote">
|
||
<dt class="label" id="id14">[<a href="#id1">1</a>]</dt>
|
||
<dd>Python-Dev mailing list: <a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/thread/ZLVPD2OISI7M4POMTR2FCQTE6TPMPTO3/">The repr of a sentinel</a></aside>
|
||
<aside class="footnote brackets" id="id15" role="doc-footnote">
|
||
<dt class="label" id="id15">[2]<em> (<a href='#id2'>1</a>, <a href='#id5'>2</a>) </em></dt>
|
||
<dd>Python-Dev mailing list: <a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/">“The stdlib contains tons of sentinels”</a></aside>
|
||
<aside class="footnote brackets" id="id16" role="doc-footnote">
|
||
<dt class="label" id="id16">[<a href="#id3">3</a>]</dt>
|
||
<dd><a class="reference external" href="https://bugs.python.org/issue44123">bpo-44123: Make function parameter sentinel values true singletons</a></aside>
|
||
<aside class="footnote brackets" id="id17" role="doc-footnote">
|
||
<dt class="label" id="id17">[4]<em> (<a href='#id4'>1</a>, <a href='#id9'>2</a>, <a href='#id10'>3</a>, <a href='#id11'>4</a>) </em></dt>
|
||
<dd>discuss.python.org Poll: <a class="reference external" href="https://discuss.python.org/t/sentinel-values-in-the-stdlib/8810/">Sentinel Values in the Stdlib</a></aside>
|
||
<aside class="footnote brackets" id="id18" role="doc-footnote">
|
||
<dt class="label" id="id18">[<a href="#id6">5</a>]</dt>
|
||
<dd><a class="reference external" href="https://pypi.org/project/sentinels/">The “sentinels” package on PyPI</a></aside>
|
||
<aside class="footnote brackets" id="id19" role="doc-footnote">
|
||
<dt class="label" id="id19">[<a href="#id7">6</a>]</dt>
|
||
<dd><a class="reference external" href="https://pypi.org/project/sentinel/">The “sentinel” package on PyPI</a></aside>
|
||
<aside class="footnote brackets" id="id20" role="doc-footnote">
|
||
<dt class="label" id="id20">[7]<em> (<a href='#id8'>1</a>, <a href='#id12'>2</a>) </em></dt>
|
||
<dd><a class="reference external" href="https://github.com/taleinat/python-stdlib-sentinels">Reference implementation at the taleinat/python-stdlib-sentinels GitHub repo</a></aside>
|
||
<aside class="footnote brackets" id="id21" role="doc-footnote">
|
||
<dt class="label" id="id21">[<a href="#id13">8</a>]</dt>
|
||
<dd><a class="reference external" href="https://mail.python.org/archives/list/typing-sig@python.org/thread/NDEJ7UCDPINP634GXWDARVMTGDVSNBKV/#LVCPTY26JQJW7NKGKGAZXHQKWVW7GOGL">Discussion thread about type signatures for these sentinels on the typing-sig mailing list</a></aside>
|
||
</aside>
|
||
</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-0661.rst">https://github.com/python/peps/blob/main/peps/pep-0661.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0661.rst">2024-08-06 12:31:33 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="#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="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#use-notgiven-object">Use <code class="docutils literal notranslate"><span class="pre">NotGiven</span> <span class="pre">=</span> <span class="pre">object()</span></code></a></li>
|
||
<li><a class="reference internal" href="#add-a-single-new-sentinel-value-such-as-missing-or-sentinel">Add a single new sentinel value, such as <code class="docutils literal notranslate"><span class="pre">MISSING</span></code> or <code class="docutils literal notranslate"><span class="pre">Sentinel</span></code></a></li>
|
||
<li><a class="reference internal" href="#use-the-existing-ellipsis-sentinel-value">Use the existing <code class="docutils literal notranslate"><span class="pre">Ellipsis</span></code> sentinel value</a></li>
|
||
<li><a class="reference internal" href="#use-a-single-valued-enum">Use a single-valued enum</a></li>
|
||
<li><a class="reference internal" href="#a-sentinel-class-decorator">A sentinel class decorator</a></li>
|
||
<li><a class="reference internal" href="#using-class-objects">Using class objects</a></li>
|
||
<li><a class="reference internal" href="#define-a-recommended-standard-idiom-without-supplying-an-implementation">Define a recommended “standard” idiom, without supplying an implementation</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#additional-notes">Additional Notes</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-0661.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> |