[Python-Dev] PEP 463: Exception-catching expressions

Chris Angelico rosuav at gmail.com
Fri Feb 21 16:27:13 CET 2014

On Sat, Feb 22, 2014 at 1:03 AM, Eli Bendersky <eliben at gmail.com> wrote:
> Chris, while I also commend you for the comprehensive PEP, I'm -1 on the
> proposal, for two main reasons:
> 1. Many proposals suggest new syntax to gain some succinctness. Each has to
> be judged for its own merits, and in this case IMHO the cons eclipse the
> pros. I don't think this will save a lot of code in a typical
> well-structured program - maybe a few lines out of hundreds. On the other
> hand, it adds yet another syntax to remember and understand, which is not
> the Pythonic way.

It doesn't need to save a huge number of lines. Just like lambda and
the if/else expression, it's there as a tool - if it makes your code
easier to read, it's a good too, and if it makes it harder, then don't
use it. Yes, it's more to learn, but so is the proliferation of ad-hoc
alternatives, several of which are listed in the 'Motivation' section
at the top.

> 2. Worse, this idea subverts exceptions to control flow, which is not only
> un-Pythonic but also against the accepted practices of programming in
> general. Here, the comparison to PEP 308 is misguided. PEP 308, whatever
> syntax it adds, still remains within the domain of normal control flow. PEP
> 463, OTOH, makes it deliberately easy to make exceptions part of
> non-exceptional code, encouraging very bad programming practices.

Again, I point to the current alternatives, including:

* dict[key] -> dict.get(key, default)

* min(sequence) -> min(sequence, default=default), as of 3.4

Both offer a way to either get an exception back or use a (usually
constant) default value. In each case, the author of the
class/function had to cater to the fact that some callers might want
it to not raise an exception. The alternative is to always use the
full try/except block, which leads to questions like "How can I keep
going after an exception?" with code like this:

    spam = d["spam"]
    ham = d["ham"]
    eggs = d["eggs"]
    sausage = d["sausage"]
except KeyError:
    thing_that_failed = ""

The dict class offers a way to avoid the exception here, by showing
that it's non-exceptional:

spam = d.get("spam","")
ham = d.get("ham","")
eggs = d.get("eggs","")
sausage = d.get("sausage","")

But not everything does. Writing that with just exception handling
looks like this:

    spam = d["spam"]
except KeyError:
    span = ""
    ham = d["ham"]
except KeyError:
    ham = ""
    eggs = d["eggs"]
except KeyError:
    eggs = ""
    sausage = d["sausage"]
except KeyError:
    sausage = ""

with extreme likelihood of an error - do you see what I got wrong
there? With inline exception handling, d could be a custom class that
simply defines [...] to raise KeyError on unknowns, and the code can
be written thus:

spam = d["spam"] except KeyError: ""
ham = d["ham"] except KeyError: ""
eggs = d["eggs"] except KeyError: ""
sausage = d["sausage"] except KeyError: ""

It's still a bit repetitive, but that's hard to avoid. And it puts the
exception handling at the exact point where it stops being exceptional
and starts being normal - exactly as the try/except statement should.


More information about the Python-Dev mailing list