mirror of https://github.com/python/peps
979 lines
91 KiB
HTML
979 lines
91 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 463 – Exception-catching expressions | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0463/">
|
||
<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 463 – Exception-catching expressions | peps.python.org'>
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0463/">
|
||
<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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 463</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 463 – Exception-catching expressions</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Chris Angelico <rosuav at gmail.com></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Created<span class="colon">:</span></dt>
|
||
<dd class="field-even">15-Feb-2014</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">3.5</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">20-Feb-2014, 16-Feb-2014</dd>
|
||
<dt class="field-odd">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2014-March/133118.html">Python-Dev message</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#rejection-notice">Rejection Notice</a></li>
|
||
<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="#proposal">Proposal</a></li>
|
||
<li><a class="reference internal" href="#alternative-proposals">Alternative Proposals</a></li>
|
||
<li><a class="reference internal" href="#example-usage">Example usage</a><ul>
|
||
<li><a class="reference internal" href="#narrowing-of-exception-catching-scope">Narrowing of exception-catching scope</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#comparisons-with-other-languages">Comparisons with other languages</a></li>
|
||
<li><a class="reference internal" href="#deferred-sub-proposals">Deferred sub-proposals</a><ul>
|
||
<li><a class="reference internal" href="#multiple-except-clauses">Multiple except clauses</a></li>
|
||
<li><a class="reference internal" href="#capturing-the-exception-object">Capturing the exception object</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-sub-proposals">Rejected sub-proposals</a><ul>
|
||
<li><a class="reference internal" href="#finally-clause">finally clause</a></li>
|
||
<li><a class="reference internal" href="#bare-except-having-different-meaning">Bare except having different meaning</a></li>
|
||
<li><a class="reference internal" href="#bare-except-clauses">Bare except clauses</a></li>
|
||
<li><a class="reference internal" href="#parentheses-around-the-except-clauses">Parentheses around the except clauses</a></li>
|
||
<li><a class="reference internal" href="#short-hand-for-except-pass">Short-hand for “except: pass”</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#common-objections">Common objections</a><ul>
|
||
<li><a class="reference internal" href="#colons-always-introduce-suites">Colons always introduce suites</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="rejection-notice">
|
||
<h2><a class="toc-backref" href="#rejection-notice" role="doc-backlink">Rejection Notice</a></h2>
|
||
<p>From <a class="reference external" href="https://mail.python.org/pipermail/python-dev/2014-March/133118.html">https://mail.python.org/pipermail/python-dev/2014-March/133118.html</a>:</p>
|
||
<p>“””
|
||
I want to reject this PEP. I think the proposed syntax is acceptable given
|
||
the desired semantics, although it’s still a bit jarring. It’s probably no
|
||
worse than the colon used with lambda (which echoes the colon used in a def
|
||
just like the colon here echoes the one in a try/except) and definitely
|
||
better than the alternatives listed.</p>
|
||
<p>But the thing I can’t get behind are the motivation and rationale. I don’t
|
||
think that e.g. dict.get() would be unnecessary once we have except
|
||
expressions, and I disagree with the position that EAFP is better than
|
||
LBYL, or “generally recommended” by Python. (Where do you get that? From
|
||
the same sources that are so obsessed with DRY they’d rather introduce a
|
||
higher-order-function than repeat one line of code? :-)</p>
|
||
<p>This is probably the most you can get out of me as far as a pronouncement.
|
||
Given that the language summit is coming up I’d be happy to dive deeper in
|
||
my reasons for rejecting it there (if there’s demand).</p>
|
||
<p>I do think that (apart from never explaining those dreadful acronyms :-)
|
||
this was a well-written and well-researched PEP, and I think you’ve done a
|
||
great job moderating the discussion, collecting objections, reviewing
|
||
alternatives, and everything else that is required to turn a heated debate
|
||
into a PEP. Well done Chris (and everyone who helped), and good luck with
|
||
your next PEP!
|
||
“””</p>
|
||
</section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>Just as <a class="pep reference internal" href="../pep-0308/" title="PEP 308 – Conditional Expressions">PEP 308</a> introduced a means of value-based conditions in an
|
||
expression, this system allows exception-based conditions to be used
|
||
as part of an expression.</p>
|
||
</section>
|
||
<section id="motivation">
|
||
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
|
||
<p>A number of functions and methods have parameters which will cause
|
||
them to return a specified value instead of raising an exception. The
|
||
current system is ad-hoc and inconsistent, and requires that each
|
||
function be individually written to have this functionality; not all
|
||
support this.</p>
|
||
<ul class="simple">
|
||
<li>dict.get(key, default) - second positional argument in place of
|
||
KeyError</li>
|
||
<li>next(iter, default) - second positional argument in place of
|
||
StopIteration</li>
|
||
<li>list.pop() - no way to return a default</li>
|
||
<li>seq[index] - no way to handle a bounds error</li>
|
||
<li>min(sequence, default=default) - keyword argument in place of
|
||
ValueError</li>
|
||
<li>statistics.mean(data) - no way to handle an empty iterator</li>
|
||
</ul>
|
||
<p>Had this facility existed early in Python’s history, there would have been
|
||
no need to create dict.get() and related methods; the one obvious way to
|
||
handle an absent key would be to respond to the exception. One method is
|
||
written which signals the absence in one way, and one consistent technique
|
||
is used to respond to the absence. Instead, we have dict.get(), and as of
|
||
Python 3.4, we also have min(… default=default), and myriad others. We
|
||
have a LBYL syntax for testing inside an expression, but there is currently
|
||
no EAFP notation; compare the following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># LBYL:</span>
|
||
<span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">dic</span><span class="p">:</span>
|
||
<span class="n">process</span><span class="p">(</span><span class="n">dic</span><span class="p">[</span><span class="n">key</span><span class="p">])</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">process</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
|
||
<span class="c1"># As an expression:</span>
|
||
<span class="n">process</span><span class="p">(</span><span class="n">dic</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">dic</span> <span class="k">else</span> <span class="kc">None</span><span class="p">)</span>
|
||
|
||
<span class="c1"># EAFP:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">process</span><span class="p">(</span><span class="n">dic</span><span class="p">[</span><span class="n">key</span><span class="p">])</span>
|
||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||
<span class="n">process</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
|
||
<span class="c1"># As an expression:</span>
|
||
<span class="n">process</span><span class="p">(</span><span class="n">dic</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Python generally recommends the EAFP policy, but must then proliferate
|
||
utility functions like dic.get(key,None) to enable this.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>The current system requires that a function author predict the need
|
||
for a default, and implement support for it. If this is not done, a
|
||
full try/except block is needed.</p>
|
||
<p>Since try/except is a statement, it is impossible to catch exceptions
|
||
in the middle of an expression. Just as if/else does for conditionals
|
||
and lambda does for function definitions, so does this allow exception
|
||
catching in an expression context.</p>
|
||
<p>This provides a clean and consistent way for a function to provide a
|
||
default: it simply raises an appropriate exception, and the caller
|
||
catches it.</p>
|
||
<p>With some situations, an LBYL technique can be used (checking if some
|
||
sequence has enough length before indexing into it, for instance). This is
|
||
not safe in all cases, but as it is often convenient, programmers will be
|
||
tempted to sacrifice the safety of EAFP in favour of the notational brevity
|
||
of LBYL. Additionally, some LBYL techniques (eg involving getattr with
|
||
three arguments) warp the code into looking like literal strings rather
|
||
than attribute lookup, which can impact readability. A convenient EAFP
|
||
notation solves all of this.</p>
|
||
<p>There’s no convenient way to write a helper function to do this; the
|
||
nearest is something ugly using either lambda:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">except_</span><span class="p">(</span><span class="n">expression</span><span class="p">,</span> <span class="n">exception_list</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">expression</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="n">exception_list</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">default</span><span class="p">()</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">except_</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="mi">1</span><span class="o">/</span><span class="n">x</span><span class="p">,</span> <span class="ne">ZeroDivisionError</span><span class="p">,</span> <span class="k">lambda</span><span class="p">:</span> <span class="nb">float</span><span class="p">(</span><span class="s2">"nan"</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>which is clunky, and unable to handle multiple exception clauses; or
|
||
eval:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">except_</span><span class="p">(</span><span class="n">expression</span><span class="p">,</span> <span class="n">exception_list</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="nb">eval</span><span class="p">(</span><span class="n">expression</span><span class="p">,</span> <span class="n">globals_of_caller</span><span class="p">(),</span> <span class="n">locals_of_caller</span><span class="p">())</span>
|
||
<span class="k">except</span> <span class="n">exception_list</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
|
||
<span class="n">l</span> <span class="o">=</span> <span class="n">locals_of_caller</span><span class="p">()</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||
<span class="n">l</span><span class="p">[</span><span class="s1">'exc'</span><span class="p">]</span> <span class="o">=</span> <span class="n">exc</span>
|
||
<span class="k">return</span> <span class="nb">eval</span><span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">globals_of_caller</span><span class="p">(),</span> <span class="n">l</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">globals_of_caller</span><span class="p">():</span>
|
||
<span class="k">return</span> <span class="n">sys</span><span class="o">.</span><span class="n">_getframe</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">f_globals</span>
|
||
|
||
<span class="k">def</span> <span class="nf">locals_of_caller</span><span class="p">():</span>
|
||
<span class="k">return</span> <span class="n">sys</span><span class="o">.</span><span class="n">_getframe</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">f_locals</span>
|
||
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">except_</span><span class="p">(</span><span class="s2">"""1/x"""</span><span class="p">,</span><span class="ne">ZeroDivisionError</span><span class="p">,</span><span class="s2">""" "Can't divide by zero" """</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>which is even clunkier, and relies on implementation-dependent hacks.
|
||
(Writing globals_of_caller() and locals_of_caller() for interpreters
|
||
other than CPython is left as an exercise for the reader.)</p>
|
||
<p>Raymond Hettinger <a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-February/025443.html">expresses</a> a desire for such a consistent
|
||
API. Something similar has been <a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2013-March/019760.html">requested</a> <a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2009-August/005441.html">multiple</a> <a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2008-August/001801.html">times</a>
|
||
in the past.</p>
|
||
</section>
|
||
<section id="proposal">
|
||
<h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2>
|
||
<p>Just as the ‘or’ operator and the three part ‘if-else’ expression give
|
||
short circuiting methods of catching a falsy value and replacing it,
|
||
this syntax gives a short-circuiting method of catching an exception
|
||
and replacing it.</p>
|
||
<p>This currently works:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">lst</span> <span class="o">=</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="kc">None</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">lst</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="ow">or</span> <span class="s2">"No value"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The proposal adds this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">lst</span> <span class="o">=</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="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">lst</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span> <span class="s2">"No value"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Specifically, the syntax proposed is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="n">exception_list</span><span class="p">:</span> <span class="n">default</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>where expr, exception_list, and default are all expressions. First,
|
||
expr is evaluated. If no exception is raised, its value is the value
|
||
of the overall expression. If any exception is raised, exception_list
|
||
is evaluated, and should result in either a type or a tuple, just as
|
||
with the statement form of try/except. Any matching exception will
|
||
result in the corresponding default expression being evaluated and
|
||
becoming the value of the expression. As with the statement form of
|
||
try/except, non-matching exceptions will propagate upward.</p>
|
||
<p>Parentheses are required around the entire expression, unless they
|
||
would be completely redundant, according to the same rules as generator
|
||
expressions follow. This guarantees correct interpretation of nested
|
||
except-expressions, and allows for future expansion of the syntax -
|
||
see below on multiple except clauses.</p>
|
||
<p>Note that the current proposal does not allow the exception object to
|
||
be captured. Where this is needed, the statement form must be used.
|
||
(See below for discussion and elaboration on this.)</p>
|
||
<p>This ternary operator would be between lambda and if/else in
|
||
precedence.</p>
|
||
<p>Consider this example of a two-level cache:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">sequence</span><span class="p">:</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">lvl1</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="p">(</span><span class="n">lvl2</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="n">f</span><span class="p">(</span><span class="n">key</span><span class="p">)))</span>
|
||
<span class="c1"># do something with x</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This cannot be rewritten as:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">lvl1</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">lvl2</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">f</span><span class="p">(</span><span class="n">key</span><span class="p">)))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>which, despite being shorter, defeats the purpose of the cache, as it must
|
||
calculate a default value to pass to get(). The .get() version calculates
|
||
backwards; the exception-testing version calculates forwards, as would be
|
||
expected. The nearest useful equivalent would be:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">lvl1</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="ow">or</span> <span class="n">lvl2</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="ow">or</span> <span class="n">f</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>which depends on the values being nonzero, as well as depending on the cache
|
||
object supporting this functionality.</p>
|
||
</section>
|
||
<section id="alternative-proposals">
|
||
<h2><a class="toc-backref" href="#alternative-proposals" role="doc-backlink">Alternative Proposals</a></h2>
|
||
<p>Discussion on python-ideas brought up the following syntax suggestions:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="n">default</span> <span class="k">if</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="n">default</span> <span class="k">for</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="n">default</span> <span class="kn">from</span> <span class="nn">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span> <span class="k">return</span> <span class="n">default</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="p">(</span><span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]:</span> <span class="n">default</span><span class="p">)</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span> <span class="k">try</span> <span class="n">default</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span> <span class="k">continue</span> <span class="k">with</span> <span class="n">default</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">default</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span> <span class="k">else</span> <span class="n">expr</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="k">try</span> <span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]:</span> <span class="n">default</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="n">default</span> <span class="c1"># Catches anything</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span><span class="p">(</span><span class="ne">Exception</span><span class="p">)</span> <span class="n">default</span> <span class="c1"># Catches only the named type(s)</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">default</span> <span class="k">if</span> <span class="n">expr</span> <span class="k">raise</span> <span class="ne">Exception</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="ow">or</span> <span class="k">else</span> <span class="n">default</span> <span class="k">if</span> <span class="ne">Exception</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span> <span class="o">-></span> <span class="n">default</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]</span> <span class="k">pass</span> <span class="n">default</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>It has also been suggested that a new keyword be created, rather than
|
||
reusing an existing one. Such proposals fall into the same structure
|
||
as the last form, but with a different keyword in place of ‘pass’.
|
||
Suggestions include ‘then’, ‘when’, and ‘use’. Also, in the context of
|
||
the “default if expr raise Exception” proposal, it was suggested that a
|
||
new keyword “raises” be used.</p>
|
||
<p>All forms involving the ‘as’ capturing clause have been deferred from
|
||
this proposal in the interests of simplicity, but are preserved in the
|
||
table above as an accurate record of suggestions.</p>
|
||
<p>The four forms most supported by this proposal are, in order:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span> <span class="n">default</span><span class="p">)</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="o">-></span> <span class="n">default</span><span class="p">)</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">pass</span> <span class="n">default</span><span class="p">)</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="n">then</span> <span class="n">default</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>All four maintain left-to-right evaluation order: first the base expression,
|
||
then the exception list, and lastly the default. This is important, as the
|
||
expressions are evaluated lazily. By comparison, several of the ad-hoc
|
||
alternatives listed above must (by the nature of functions) evaluate their
|
||
default values eagerly. The preferred form, using the colon, parallels
|
||
try/except by using “except exception_list:”, and parallels lambda by having
|
||
“keyword name_list: subexpression”; it also can be read as mapping Exception
|
||
to the default value, dict-style. Using the arrow introduces a token many
|
||
programmers will not be familiar with, and which currently has no similar
|
||
meaning, but is otherwise quite readable. The English word “pass” has a
|
||
vaguely similar meaning (consider the common usage “pass by value/reference”
|
||
for function arguments), and “pass” is already a keyword, but as its meaning
|
||
is distinctly unrelated, this may cause confusion. Using “then” makes sense
|
||
in English, but this introduces a new keyword to the language - albeit one
|
||
not in common use, but a new keyword all the same.</p>
|
||
<p>Left to right evaluation order is extremely important to readability, as it
|
||
parallels the order most expressions are evaluated. Alternatives such as:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="n">default</span> <span class="k">if</span> <span class="ne">Exception</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>break this, by first evaluating the two ends, and then coming to the middle;
|
||
while this may not seem terrible (as the exception list will usually be a
|
||
constant), it does add to the confusion when multiple clauses meet, either
|
||
with multiple except/if or with the existing if/else, or a combination.
|
||
Using the preferred order, subexpressions will always be evaluated from
|
||
left to right, no matter how the syntax is nested.</p>
|
||
<p>Keeping the existing notation, but shifting the mandatory parentheses, we
|
||
have the following suggestion:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="p">(</span><span class="ne">Exception</span><span class="p">:</span> <span class="n">default</span><span class="p">)</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span><span class="p">(</span><span class="ne">Exception</span><span class="p">:</span> <span class="n">default</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This is reminiscent of a function call, or a dict initializer. The colon
|
||
cannot be confused with introducing a suite, but on the other hand, the new
|
||
syntax guarantees lazy evaluation, which a dict does not. The potential
|
||
to reduce confusion is considered unjustified by the corresponding potential
|
||
to increase it.</p>
|
||
</section>
|
||
<section id="example-usage">
|
||
<h2><a class="toc-backref" href="#example-usage" role="doc-backlink">Example usage</a></h2>
|
||
<p>For each example, an approximately-equivalent statement form is given,
|
||
to show how the expression will be parsed. These are not always
|
||
strictly equivalent, but will accomplish the same purpose. It is NOT
|
||
safe for the interpreter to translate one into the other.</p>
|
||
<p>A number of these examples are taken directly from the Python standard
|
||
library, with file names and line numbers correct as of early Feb 2014.
|
||
Many of these patterns are extremely common.</p>
|
||
<p>Retrieve an argument, defaulting to None:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cond</span> <span class="o">=</span> <span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/pdb.py:803:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">cond</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
|
||
<span class="n">cond</span> <span class="o">=</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Fetch information from the system if available:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pwd</span> <span class="o">=</span> <span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">()</span> <span class="k">except</span> <span class="ne">OSError</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/tkinter/filedialog.py:210:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">pwd</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="ne">OSError</span><span class="p">:</span>
|
||
<span class="n">pwd</span> <span class="o">=</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Attempt a translation, falling back on the original:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">e</span><span class="o">.</span><span class="n">widget</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_nametowidget</span><span class="p">(</span><span class="n">W</span><span class="p">)</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="n">W</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/tkinter/__init__.py:1222:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">e</span><span class="o">.</span><span class="n">widget</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nametowidget</span><span class="p">(</span><span class="n">W</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||
<span class="n">e</span><span class="o">.</span><span class="n">widget</span> <span class="o">=</span> <span class="n">W</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Read from an iterator, continuing with blank lines once it’s
|
||
exhausted:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">line</span> <span class="o">=</span> <span class="p">(</span><span class="n">readline</span><span class="p">()</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="s1">''</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/lib2to3/pgen2/tokenize.py:370:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">line</span> <span class="o">=</span> <span class="n">readline</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span>
|
||
<span class="n">line</span> <span class="o">=</span> <span class="s1">''</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Retrieve platform-specific information (note the DRY improvement);
|
||
this particular example could be taken further, turning a series of
|
||
separate assignments into a single large dict initialization:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># sys.abiflags may not be defined on all platforms.</span>
|
||
<span class="n">_CONFIG_VARS</span><span class="p">[</span><span class="s1">'abiflags'</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">abiflags</span> <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span> <span class="s1">''</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/sysconfig.py:529:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">_CONFIG_VARS</span><span class="p">[</span><span class="s1">'abiflags'</span><span class="p">]</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">abiflags</span>
|
||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||
<span class="c1"># sys.abiflags may not be defined on all platforms.</span>
|
||
<span class="n">_CONFIG_VARS</span><span class="p">[</span><span class="s1">'abiflags'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">''</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Retrieve an indexed item, defaulting to None (similar to dict.get):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">getNamedItem</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="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/xml/dom/minidom.py:573:</span>
|
||
<span class="k">def</span> <span class="nf">getNamedItem</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="k">try</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_attrs</span><span class="p">[</span><span class="n">name</span><span class="p">]</span>
|
||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Translate numbers to names, falling back on the numbers:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">g</span> <span class="o">=</span> <span class="p">(</span><span class="n">grp</span><span class="o">.</span><span class="n">getgrnam</span><span class="p">(</span><span class="n">tarinfo</span><span class="o">.</span><span class="n">gname</span><span class="p">)[</span><span class="mi">2</span><span class="p">]</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="n">tarinfo</span><span class="o">.</span><span class="n">gid</span><span class="p">)</span>
|
||
<span class="n">u</span> <span class="o">=</span> <span class="p">(</span><span class="n">pwd</span><span class="o">.</span><span class="n">getpwnam</span><span class="p">(</span><span class="n">tarinfo</span><span class="o">.</span><span class="n">uname</span><span class="p">)[</span><span class="mi">2</span><span class="p">]</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="n">tarinfo</span><span class="o">.</span><span class="n">uid</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/tarfile.py:2198:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">g</span> <span class="o">=</span> <span class="n">grp</span><span class="o">.</span><span class="n">getgrnam</span><span class="p">(</span><span class="n">tarinfo</span><span class="o">.</span><span class="n">gname</span><span class="p">)[</span><span class="mi">2</span><span class="p">]</span>
|
||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||
<span class="n">g</span> <span class="o">=</span> <span class="n">tarinfo</span><span class="o">.</span><span class="n">gid</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">u</span> <span class="o">=</span> <span class="n">pwd</span><span class="o">.</span><span class="n">getpwnam</span><span class="p">(</span><span class="n">tarinfo</span><span class="o">.</span><span class="n">uname</span><span class="p">)[</span><span class="mi">2</span><span class="p">]</span>
|
||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||
<span class="n">u</span> <span class="o">=</span> <span class="n">tarinfo</span><span class="o">.</span><span class="n">uid</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Look up an attribute, falling back on a default:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mode</span> <span class="o">=</span> <span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">mode</span> <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span> <span class="s1">'rb'</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Lib/aifc.py:882:</span>
|
||
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s1">'mode'</span><span class="p">):</span>
|
||
<span class="n">mode</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">mode</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">mode</span> <span class="o">=</span> <span class="s1">'rb'</span>
|
||
|
||
<span class="k">return</span> <span class="p">(</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="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span> <span class="kc">None</span><span class="p">)</span>
|
||
<span class="c1"># Lib/inspect.py:1350:</span>
|
||
<span class="k">return</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="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">sys</span><span class="p">,</span> <span class="s2">"_getframe"</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Perform some lengthy calculations in EAFP mode, handling division by
|
||
zero as a sort of sticky NaN:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">calculate</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">except</span> <span class="ne">ZeroDivisionError</span><span class="p">:</span> <span class="nb">float</span><span class="p">(</span><span class="s2">"nan"</span><span class="p">))</span>
|
||
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">calculate</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">ZeroDivisionError</span><span class="p">:</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="s2">"nan"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Calculate the mean of a series of numbers, falling back on zero:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">statistics</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">lst</span><span class="p">)</span> <span class="k">except</span> <span class="n">statistics</span><span class="o">.</span><span class="n">StatisticsError</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
|
||
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">statistics</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">lst</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="n">statistics</span><span class="o">.</span><span class="n">StatisticsError</span><span class="p">:</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Looking up objects in a sparse list of overrides:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">overrides</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="ow">or</span> <span class="n">default</span> <span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span> <span class="n">default</span><span class="p">)</span><span class="o">.</span><span class="n">ping</span><span class="p">()</span>
|
||
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="p">(</span><span class="n">overrides</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="ow">or</span> <span class="n">default</span><span class="p">)</span><span class="o">.</span><span class="n">ping</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
|
||
<span class="n">default</span><span class="o">.</span><span class="n">ping</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<section id="narrowing-of-exception-catching-scope">
|
||
<h3><a class="toc-backref" href="#narrowing-of-exception-catching-scope" role="doc-backlink">Narrowing of exception-catching scope</a></h3>
|
||
<p>The following examples, taken directly from Python’s standard library,
|
||
demonstrate how the scope of the try/except can be conveniently narrowed.
|
||
To do this with the statement form of try/except would require a temporary
|
||
variable, but it’s far cleaner as an expression.</p>
|
||
<p>Lib/ipaddress.py:343:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
|
||
<span class="n">ips</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ip</span><span class="o">.</span><span class="n">ip</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||
<span class="n">ips</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ip</span><span class="o">.</span><span class="n">network_address</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Becomes:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">ips</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ip</span><span class="o">.</span><span class="n">ip</span> <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span> <span class="n">ip</span><span class="o">.</span><span class="n">network_address</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The expression form is nearly equivalent to this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
|
||
<span class="n">_</span> <span class="o">=</span> <span class="n">ip</span><span class="o">.</span><span class="n">ip</span>
|
||
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
|
||
<span class="n">_</span> <span class="o">=</span> <span class="n">ip</span><span class="o">.</span><span class="n">network_address</span>
|
||
<span class="n">ips</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">_</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Lib/tempfile.py:130:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
|
||
<span class="n">dirlist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">_os</span><span class="o">.</span><span class="n">getcwd</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">OSError</span><span class="p">):</span>
|
||
<span class="n">dirlist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">_os</span><span class="o">.</span><span class="n">curdir</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Becomes:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">dirlist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">_os</span><span class="o">.</span><span class="n">getcwd</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">OSError</span><span class="p">):</span> <span class="n">_os</span><span class="o">.</span><span class="n">curdir</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Lib/asyncore.py:264:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
|
||
<span class="n">status</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">'</span><span class="si">%s</span><span class="s1">:</span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">addr</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span>
|
||
<span class="n">status</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">addr</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Becomes:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">status</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">'</span><span class="si">%s</span><span class="s1">:</span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">addr</span> <span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span> <span class="nb">repr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">addr</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In each case, the narrowed scope of the try/except ensures that an unexpected
|
||
exception (for instance, AttributeError if “append” were misspelled) does not
|
||
get caught by the same handler. This is sufficiently unlikely to be reason
|
||
to break the call out into a separate line (as per the five line example
|
||
above), but it is a small benefit gained as a side-effect of the conversion.</p>
|
||
</section>
|
||
</section>
|
||
<section id="comparisons-with-other-languages">
|
||
<h2><a class="toc-backref" href="#comparisons-with-other-languages" role="doc-backlink">Comparisons with other languages</a></h2>
|
||
<p>(With thanks to Andrew Barnert for compiling this section. Note that the
|
||
examples given here do not reflect the current version of the proposal,
|
||
and need to be edited.)</p>
|
||
<p><a class="reference external" href="http://www.skorks.com/2009/09/ruby-exceptions-and-exception-handling/">Ruby’s</a> “begin…rescue…rescue…else…ensure…end” is an expression
|
||
(potentially with statements inside it). It has the equivalent of an “as”
|
||
clause, and the equivalent of bare except. And it uses no punctuation or
|
||
keyword between the bare except/exception class/exception class with as
|
||
clause and the value. (And yes, it’s ambiguous unless you understand
|
||
Ruby’s statement/expression rules.)</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">begin</span> <span class="n">computation</span><span class="p">()</span> <span class="n">rescue</span> <span class="n">MyException</span> <span class="o">=></span> <span class="n">e</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="n">end</span><span class="p">;</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">begin</span> <span class="n">computation</span><span class="p">()</span> <span class="n">rescue</span> <span class="n">MyException</span> <span class="n">default</span><span class="p">()</span> <span class="n">end</span><span class="p">;</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">begin</span> <span class="n">computation</span><span class="p">()</span> <span class="n">rescue</span> <span class="n">default</span><span class="p">()</span> <span class="n">end</span><span class="p">;</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">begin</span> <span class="n">computation</span><span class="p">()</span> <span class="n">rescue</span> <span class="n">MyException</span> <span class="n">default</span><span class="p">()</span> <span class="n">rescue</span> <span class="n">OtherException</span> <span class="n">other</span><span class="p">()</span> <span class="n">end</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In terms of this PEP:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">MyException</span> <span class="k">as</span> <span class="n">e</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">MyException</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">MyException</span> <span class="n">default</span><span class="p">()</span> <span class="k">except</span> <span class="n">OtherException</span> <span class="n">other</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><a class="reference external" href="http://erlang.org/doc/reference_manual/expressions.html#id79284">Erlang</a> has a try expression that looks like this</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="n">catch</span> <span class="n">MyException</span><span class="p">:</span><span class="n">e</span> <span class="o">-></span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="n">end</span><span class="p">;</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="n">catch</span> <span class="n">MyException</span><span class="p">:</span><span class="n">e</span> <span class="o">-></span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">);</span> <span class="n">OtherException</span><span class="p">:</span><span class="n">e</span> <span class="o">-></span> <span class="n">other</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="n">end</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The class and “as” name are mandatory, but you can use “_” for either.
|
||
There’s also an optional “when” guard on each, and a “throw” clause that
|
||
you can catch, which I won’t get into. To handle multiple exceptions,
|
||
you just separate the clauses with semicolons, which I guess would map
|
||
to commas in Python. So:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">MyException</span> <span class="k">as</span> <span class="n">e</span> <span class="o">-></span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">MyException</span> <span class="k">as</span> <span class="n">e</span> <span class="o">-></span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">),</span> <span class="n">OtherException</span> <span class="k">as</span> <span class="n">e</span><span class="o">-></span><span class="n">other_default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Erlang also has a “catch” expression, which, despite using the same keyword,
|
||
is completely different, and you don’t want to know about it.</p>
|
||
<p>The ML family has two different ways of dealing with this, “handle” and
|
||
“try”; the difference between the two is that “try” pattern-matches the
|
||
exception, which gives you the effect of multiple except clauses and as
|
||
clauses. In either form, the handler clause is punctuated by “=>” in
|
||
some dialects, “->” in others.</p>
|
||
<p>To avoid confusion, I’ll write the function calls in Python style.</p>
|
||
<p>Here’s <a class="reference external" href="http://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm">SML’s</a> “handle”</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">computation</span><span class="p">()</span> <span class="n">handle</span> <span class="n">MyException</span> <span class="o">=></span> <span class="n">default</span><span class="p">();;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here’s <a class="reference external" href="http://www2.lib.uchicago.edu/keith/ocaml-class/exceptions.html">OCaml’s</a> “try”</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">let</span> <span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="k">with</span> <span class="n">MyException</span> <span class="n">explanation</span> <span class="o">-></span> <span class="n">default</span><span class="p">(</span><span class="n">explanation</span><span class="p">);;</span>
|
||
|
||
<span class="n">let</span> <span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="k">with</span>
|
||
|
||
<span class="n">MyException</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">-></span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
<span class="o">|</span> <span class="n">MyOtherException</span><span class="p">()</span> <span class="o">-></span> <span class="n">other_default</span><span class="p">()</span>
|
||
<span class="o">|</span> <span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">-></span> <span class="n">fallback</span><span class="p">(</span><span class="n">e</span><span class="p">);;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In terms of this PEP, these would be something like:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">MyException</span> <span class="o">=></span> <span class="n">default</span><span class="p">()</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="k">except</span> <span class="n">MyException</span> <span class="n">e</span> <span class="o">-></span> <span class="n">default</span><span class="p">()</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="k">try</span> <span class="n">computation</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="n">MyException</span> <span class="k">as</span> <span class="n">e</span> <span class="o">-></span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="n">MyOtherException</span> <span class="o">-></span> <span class="n">other_default</span><span class="p">()</span>
|
||
<span class="k">except</span> <span class="ne">BaseException</span> <span class="k">as</span> <span class="n">e</span> <span class="o">-></span> <span class="n">fallback</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Many ML-inspired but not-directly-related languages from academia mix things
|
||
up, usually using more keywords and fewer symbols. So, the <a class="reference external" href="http://mozart.github.io/mozart-v1/doc-1.4.0/tutorial/node5.html">Oz</a> would map
|
||
to Python as</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="k">try</span> <span class="n">computation</span><span class="p">()</span> <span class="n">catch</span> <span class="n">MyException</span> <span class="k">as</span> <span class="n">e</span> <span class="n">then</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Many Lisp-derived languages, like <a class="reference external" href="http://clojure.org/special_forms#Special%20Forms--(try%20expr*%20catch-clause*%20finally-clause?)">Clojure,</a> implement try/catch as special
|
||
forms (if you don’t know what that means, think function-like macros), so you
|
||
write, effectively</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">(</span><span class="n">computation</span><span class="p">(),</span> <span class="n">catch</span><span class="p">(</span><span class="n">MyException</span><span class="p">,</span> <span class="n">explanation</span><span class="p">,</span> <span class="n">default</span><span class="p">(</span><span class="n">explanation</span><span class="p">)))</span>
|
||
|
||
<span class="k">try</span><span class="p">(</span><span class="n">computation</span><span class="p">(),</span>
|
||
<span class="n">catch</span><span class="p">(</span><span class="n">MyException</span><span class="p">,</span> <span class="n">explanation</span><span class="p">,</span> <span class="n">default</span><span class="p">(</span><span class="n">explanation</span><span class="p">)),</span>
|
||
<span class="n">catch</span><span class="p">(</span><span class="n">MyOtherException</span><span class="p">,</span> <span class="n">explanation</span><span class="p">,</span> <span class="n">other_default</span><span class="p">(</span><span class="n">explanation</span><span class="p">)))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In Common Lisp, this is done with a slightly clunkier <a class="reference external" href="http://clhs.lisp.se/Body/m_hand_1.htm">“handler-case” macro,</a>
|
||
but the basic idea is the same.</p>
|
||
<p>The Lisp style is, surprisingly, used by some languages that don’t have
|
||
macros, like Lua, where <a class="reference external" href="http://www.gammon.com.au/scripts/doc.php?lua=xpcall">xpcall</a> takes functions. Writing lambdas
|
||
Python-style instead of Lua-style</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">xpcall</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">expression</span><span class="p">(),</span> <span class="k">lambda</span> <span class="n">e</span><span class="p">:</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This actually returns (true, expression()) or (false, default(e)), but I think we can ignore that part.</p>
|
||
<p>Haskell is actually similar to Lua here (except that it’s all done
|
||
with monads, of course):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">do</span> <span class="n">catch</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">expression</span><span class="p">(),</span> <span class="k">lambda</span> <span class="n">e</span><span class="p">:</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>You can write a pattern matching expression within the function to decide
|
||
what to do with it; catching and re-raising exceptions you don’t want is
|
||
cheap enough to be idiomatic.</p>
|
||
<p>But Haskell infixing makes this nicer:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>x = do expression() `catch` lambda: default()
|
||
x = do expression() `catch` lambda e: default(e)
|
||
</pre></div>
|
||
</div>
|
||
<p>And that makes the parallel between the lambda colon and the except
|
||
colon in the proposal much more obvious:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">expression</span><span class="p">()</span> <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span> <span class="n">default</span><span class="p">()</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">expression</span><span class="p">()</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="n">default</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><a class="reference external" href="http://wiki.tcl.tk/902">Tcl</a> has the other half of Lua’s xpcall; catch is a function which returns
|
||
true if an exception was caught, false otherwise, and you get the value out
|
||
in other ways. And it’s all built around the implicit quote-and-exec
|
||
that everything in Tcl is based on, making it even harder to describe in
|
||
Python terms than Lisp macros, but something like</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="p">{[</span> <span class="n">catch</span><span class="p">(</span><span class="s2">"computation()"</span><span class="p">)</span> <span class="s2">"explanation"</span><span class="p">]}</span> <span class="p">{</span> <span class="n">default</span><span class="p">(</span><span class="n">explanation</span><span class="p">)</span> <span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><a class="reference external" href="http://smalltalk.gnu.org/wiki/exceptions">Smalltalk</a> is also somewhat hard to map to Python. The basic version
|
||
would be</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">:=</span> <span class="n">computation</span><span class="p">()</span> <span class="n">on</span><span class="p">:</span><span class="n">MyException</span> <span class="n">do</span><span class="p">:</span><span class="n">default</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>… but that’s basically Smalltalk’s passing-arguments-with-colons
|
||
syntax, not its exception-handling syntax.</p>
|
||
</section>
|
||
<section id="deferred-sub-proposals">
|
||
<h2><a class="toc-backref" href="#deferred-sub-proposals" role="doc-backlink">Deferred sub-proposals</a></h2>
|
||
<section id="multiple-except-clauses">
|
||
<h3><a class="toc-backref" href="#multiple-except-clauses" role="doc-backlink">Multiple except clauses</a></h3>
|
||
<p>An examination of use-cases shows that this is not needed as often as
|
||
it would be with the statement form, and as its syntax is a point on
|
||
which consensus has not been reached, the entire feature is deferred.</p>
|
||
<p>Multiple ‘except’ keywords could be used, and they will all catch
|
||
exceptions raised in the original expression (only):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Will catch any of the listed exceptions thrown by expr;</span>
|
||
<span class="c1"># any exception thrown by a default expression will propagate.</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span>
|
||
<span class="k">except</span> <span class="n">Exception1</span><span class="p">:</span> <span class="n">default1</span>
|
||
<span class="k">except</span> <span class="n">Exception2</span><span class="p">:</span> <span class="n">default2</span>
|
||
<span class="c1"># ... except ExceptionN: defaultN</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Currently, one of the following forms must be used:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Will catch an Exception2 thrown by either expr or default1</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="n">Exception1</span><span class="p">:</span> <span class="n">default1</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="n">Exception2</span><span class="p">:</span> <span class="n">default2</span>
|
||
<span class="p">)</span>
|
||
<span class="c1"># Will catch an Exception2 thrown by default1 only</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="n">Exception1</span><span class="p">:</span>
|
||
<span class="p">(</span><span class="n">default1</span> <span class="k">except</span> <span class="n">Exception2</span><span class="p">:</span> <span class="n">default2</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Listing multiple exception clauses without parentheses is a syntax error
|
||
(see above), and so a future version of Python is free to add this feature
|
||
without breaking any existing code.</p>
|
||
</section>
|
||
<section id="capturing-the-exception-object">
|
||
<h3><a class="toc-backref" href="#capturing-the-exception-object" role="doc-backlink">Capturing the exception object</a></h3>
|
||
<p>In a try/except block, the use of ‘as’ to capture the exception object
|
||
creates a local name binding, and implicitly deletes that binding (to
|
||
avoid creating a reference loop) in a finally clause. In an expression
|
||
context, this makes little sense, and a proper sub-scope would be
|
||
required to safely capture the exception object - something akin to the
|
||
way a list comprehension is handled. However, CPython currently
|
||
implements a comprehension’s subscope with a nested function call, which
|
||
has consequences in some contexts such as class definitions, and is
|
||
therefore unsuitable for this proposal. Should there be, in future, a
|
||
way to create a true subscope (which could simplify comprehensions,
|
||
except expressions, with blocks, and possibly more), then this proposal
|
||
could be revived; until then, its loss is not a great one, as the simple
|
||
exception handling that is well suited to the expression notation used
|
||
here is generally concerned only with the type of the exception, and not
|
||
its value - further analysis below.</p>
|
||
<p>This syntax would, admittedly, allow a convenient way to capture
|
||
exceptions in interactive Python; returned values are captured by “_”,
|
||
but exceptions currently are not. This could be spelled:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="p">(</span><span class="n">expr</span> <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="n">e</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>An examination of the Python standard library shows that, while the use
|
||
of ‘as’ is fairly common (occurring in roughly one except clause in five),
|
||
it is extremely <em>uncommon</em> in the cases which could logically be converted
|
||
into the expression form. Its few uses can simply be left unchanged.
|
||
Consequently, in the interests of simplicity, the ‘as’ clause is not
|
||
included in this proposal. A subsequent Python version can add this without
|
||
breaking any existing code, as ‘as’ is already a keyword.</p>
|
||
<p>One example where this could possibly be useful is Lib/imaplib.py:568:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span> <span class="n">typ</span><span class="p">,</span> <span class="n">dat</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_simple_command</span><span class="p">(</span><span class="s1">'LOGOUT'</span><span class="p">)</span>
|
||
<span class="k">except</span><span class="p">:</span> <span class="n">typ</span><span class="p">,</span> <span class="n">dat</span> <span class="o">=</span> <span class="s1">'NO'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'</span><span class="si">%s</span><span class="s1">: </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()[:</span><span class="mi">2</span><span class="p">]]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This could become:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typ</span><span class="p">,</span> <span class="n">dat</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_simple_command</span><span class="p">(</span><span class="s1">'LOGOUT'</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">BaseException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span> <span class="p">(</span><span class="s1">'NO'</span><span class="p">,</span> <span class="s1">'</span><span class="si">%s</span><span class="s1">: </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">),</span> <span class="n">e</span><span class="p">)))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Or perhaps some other variation. This is hardly the most compelling use-case,
|
||
but an intelligent look at this code could tidy it up significantly. In the
|
||
absence of further examples showing any need of the exception object, I have
|
||
opted to defer indefinitely the recommendation.</p>
|
||
</section>
|
||
</section>
|
||
<section id="rejected-sub-proposals">
|
||
<h2><a class="toc-backref" href="#rejected-sub-proposals" role="doc-backlink">Rejected sub-proposals</a></h2>
|
||
<section id="finally-clause">
|
||
<h3><a class="toc-backref" href="#finally-clause" role="doc-backlink">finally clause</a></h3>
|
||
<p>The statement form try… finally or try… except… finally has no
|
||
logical corresponding expression form. Therefore, the finally keyword
|
||
is not a part of this proposal, in any way.</p>
|
||
</section>
|
||
<section id="bare-except-having-different-meaning">
|
||
<h3><a class="toc-backref" href="#bare-except-having-different-meaning" role="doc-backlink">Bare except having different meaning</a></h3>
|
||
<p>With several of the proposed syntaxes, omitting the exception type name
|
||
would be easy and concise, and would be tempting. For convenience’s sake,
|
||
it might be advantageous to have a bare ‘except’ clause mean something
|
||
more useful than “except BaseException”. Proposals included having it
|
||
catch Exception, or some specific set of “common exceptions” (subclasses
|
||
of a new type called ExpressionError), or have it look for a tuple named
|
||
ExpressionError in the current scope, with a built-in default such as
|
||
(ValueError, UnicodeError, AttributeError, EOFError, IOError, OSError,
|
||
LookupError, NameError, ZeroDivisionError). All of these were rejected,
|
||
for several reasons.</p>
|
||
<ul class="simple">
|
||
<li>First and foremost, consistency with the statement form of try/except
|
||
would be broken. Just as a list comprehension or ternary if expression
|
||
can be explained by “breaking it out” into its vertical statement form,
|
||
an expression-except should be able to be explained by a relatively
|
||
mechanical translation into a near-equivalent statement. Any form of
|
||
syntax common to both should therefore have the same semantics in each,
|
||
and above all should not have the subtle difference of catching more in
|
||
one than the other, as it will tend to attract unnoticed bugs.</li>
|
||
<li>Secondly, the set of appropriate exceptions to catch would itself be
|
||
a huge point of contention. It would be impossible to predict exactly
|
||
which exceptions would “make sense” to be caught; why bless some of them
|
||
with convenient syntax and not others?</li>
|
||
<li>And finally (this partly because the recommendation was that a bare
|
||
except should be actively encouraged, once it was reduced to a “reasonable”
|
||
set of exceptions), any situation where you catch an exception you don’t
|
||
expect to catch is an unnecessary bug magnet.</li>
|
||
</ul>
|
||
<p>Consequently, the use of a bare ‘except’ is down to two possibilities:
|
||
either it is syntactically forbidden in the expression form, or it is
|
||
permitted with the exact same semantics as in the statement form (namely,
|
||
that it catch BaseException and be unable to capture it with ‘as’).</p>
|
||
</section>
|
||
<section id="bare-except-clauses">
|
||
<h3><a class="toc-backref" href="#bare-except-clauses" role="doc-backlink">Bare except clauses</a></h3>
|
||
<p><a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a> rightly advises against the use of a bare ‘except’. While it is
|
||
syntactically legal in a statement, and for backward compatibility must
|
||
remain so, there is little value in encouraging its use. In an expression
|
||
except clause, “except:” is a SyntaxError; use the equivalent long-hand
|
||
form “except BaseException:” instead. A future version of Python MAY choose
|
||
to reinstate this, which can be done without breaking compatibility.</p>
|
||
</section>
|
||
<section id="parentheses-around-the-except-clauses">
|
||
<h3><a class="toc-backref" href="#parentheses-around-the-except-clauses" role="doc-backlink">Parentheses around the except clauses</a></h3>
|
||
<p>Should it be legal to parenthesize the except clauses, separately from
|
||
the expression that could raise? Example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="p">(</span>
|
||
<span class="k">except</span> <span class="n">Exception1</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]:</span> <span class="n">default1</span>
|
||
<span class="k">except</span> <span class="n">Exception2</span> <span class="p">[</span><span class="k">as</span> <span class="n">e</span><span class="p">]:</span> <span class="n">default2</span>
|
||
<span class="c1"># ... except ExceptionN [as e]: defaultN</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This is more compelling when one or both of the deferred sub-proposals
|
||
of multiple except clauses and/or exception capturing is included. In
|
||
their absence, the parentheses would be thus:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="k">except</span> <span class="n">ExceptionType</span><span class="p">:</span> <span class="n">default</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">expr</span> <span class="p">(</span><span class="k">except</span> <span class="n">ExceptionType</span><span class="p">:</span> <span class="n">default</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The advantage is minimal, and the potential to confuse a reader into
|
||
thinking the except clause is separate from the expression, or into thinking
|
||
this is a function call, makes this non-compelling. The expression can, of
|
||
course, be parenthesized if desired, as can the default:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">expr</span><span class="p">)</span> <span class="k">except</span> <span class="n">ExceptionType</span><span class="p">:</span> <span class="p">(</span><span class="n">default</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As the entire expression is now required to be in parentheses (which had not
|
||
been decided at the time when this was debated), there is less need to
|
||
delineate this section, and in many cases it would be redundant.</p>
|
||
</section>
|
||
<section id="short-hand-for-except-pass">
|
||
<h3><a class="toc-backref" href="#short-hand-for-except-pass" role="doc-backlink">Short-hand for “except: pass”</a></h3>
|
||
<p>The following was been suggested as a similar
|
||
short-hand, though not technically an expression:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">statement</span> <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span> <span class="k">pass</span>
|
||
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="n">statement</span>
|
||
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>For instance, a common use-case is attempting the removal of a file:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">os</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">some_file</span><span class="p">)</span> <span class="k">except</span> <span class="ne">OSError</span><span class="p">:</span> <span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>There is an equivalent already in Python 3.4, however, in contextlib:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">suppress</span>
|
||
<span class="k">with</span> <span class="n">suppress</span><span class="p">(</span><span class="ne">OSError</span><span class="p">):</span> <span class="n">os</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">some_file</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As this is already a single line (or two with a break after the colon),
|
||
there is little need of new syntax and a confusion of statement vs
|
||
expression to achieve this.</p>
|
||
</section>
|
||
</section>
|
||
<section id="common-objections">
|
||
<h2><a class="toc-backref" href="#common-objections" role="doc-backlink">Common objections</a></h2>
|
||
<section id="colons-always-introduce-suites">
|
||
<h3><a class="toc-backref" href="#colons-always-introduce-suites" role="doc-backlink">Colons always introduce suites</a></h3>
|
||
<p>While it is true that many of Python’s syntactic elements use the colon to
|
||
introduce a statement suite (if, while, with, for, etcetera), this is not
|
||
by any means the sole use of the colon. Currently, Python syntax includes
|
||
four cases where a colon introduces a subexpression:</p>
|
||
<ul class="simple">
|
||
<li>dict display - { … key:value … }</li>
|
||
<li>slice notation - [start:stop:step]</li>
|
||
<li>function definition - parameter : annotation</li>
|
||
<li>lambda - arg list: return value</li>
|
||
</ul>
|
||
<p>This proposal simply adds a fifth:</p>
|
||
<ul class="simple">
|
||
<li>except-expression - exception list: result</li>
|
||
</ul>
|
||
<p>Style guides and <a class="pep reference internal" href="../pep-0008/" title="PEP 8 – Style Guide for Python Code">PEP 8</a> should recommend not having the colon at the end of
|
||
a wrapped line, which could potentially look like the introduction of a
|
||
suite, but instead advocate wrapping before the exception list, keeping the
|
||
colon clearly between two expressions.</p>
|
||
</section>
|
||
</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-0463.rst">https://github.com/python/peps/blob/main/peps/pep-0463.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0463.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="#rejection-notice">Rejection Notice</a></li>
|
||
<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="#proposal">Proposal</a></li>
|
||
<li><a class="reference internal" href="#alternative-proposals">Alternative Proposals</a></li>
|
||
<li><a class="reference internal" href="#example-usage">Example usage</a><ul>
|
||
<li><a class="reference internal" href="#narrowing-of-exception-catching-scope">Narrowing of exception-catching scope</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#comparisons-with-other-languages">Comparisons with other languages</a></li>
|
||
<li><a class="reference internal" href="#deferred-sub-proposals">Deferred sub-proposals</a><ul>
|
||
<li><a class="reference internal" href="#multiple-except-clauses">Multiple except clauses</a></li>
|
||
<li><a class="reference internal" href="#capturing-the-exception-object">Capturing the exception object</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-sub-proposals">Rejected sub-proposals</a><ul>
|
||
<li><a class="reference internal" href="#finally-clause">finally clause</a></li>
|
||
<li><a class="reference internal" href="#bare-except-having-different-meaning">Bare except having different meaning</a></li>
|
||
<li><a class="reference internal" href="#bare-except-clauses">Bare except clauses</a></li>
|
||
<li><a class="reference internal" href="#parentheses-around-the-except-clauses">Parentheses around the except clauses</a></li>
|
||
<li><a class="reference internal" href="#short-hand-for-except-pass">Short-hand for “except: pass”</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#common-objections">Common objections</a><ul>
|
||
<li><a class="reference internal" href="#colons-always-introduce-suites">Colons always introduce suites</a></li>
|
||
</ul>
|
||
</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-0463.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> |