[Python-ideas] why not try without except?

Steven D'Aprano steve at pearwood.info
Sun Apr 26 04:25:03 CEST 2009


On Sat, 25 Apr 2009 06:32:07 pm spir wrote:
> Hello,
>
> In various cases, we need to do something (set or update a var or
> attribute, launch an action, compute data), or not, according to a
> condition that is a potential case of exception. Python provides the
> try...except construct to allow straightforward expression of the
> non-exceptional case without overloading the code with explicit
> checkings.
>
> Still, in the common case above, the result is
>    try:
>       <do_something>
>    except ErrorType:
>       pass


I've had a look in my code, and I very rarely have pass in the 
except-block. I don't think this is any where near as common as you 
think.


> For instance
>    try:
>       text += doc.footer
>    except ErrorType:
>       pass
> or
>    if hasattr(doc, 'footer')
>       text += doc.footer


What is ErrorType? If you mean AttributeError, say so :)

A third alternative is to make sure that doc.footer always exists, even 
if it is only the empty string, and then just write:

text += doc.footer

In many cases, I prefer that. I don't like attributes which sometimes 
exist and sometimes don't.


> Actually, this shows that 'try' alone can be considered analog to
> 'if' without 'else' clause, 

I think that is stretching the analogy past breaking point. In the 
if...else case, only one of the if-block or else-block are taken. But 
that's not true in the case of try...except: potentially both blocks 
are taken.

L = []
try:
    for x in ('1.2', '3.4', '5.6', '7..7'):
        print x
        L.append(float(x))
except ValueError:
    print '%s is not a valid float' % x
finally:
    print L


As you can see from running that code snippet, both the try and except 
blocks get run. And there is no analogy to finally in if...else.


> needed when the condition may raise an 
> exception.  Both express an optional action. This could be written 
> more directly, because we do not need the condition: ? <do_something>
>    option <do_something>
> with the meaning: "Try & do this, but if there's an exception just
> let it down."


But what exception? You have to specify what sort of exception(s) you 
want to ignore, because not all exceptions mean the same thing. 
Consider this:

try:
    text += doc.footer

# just ignore exceptions

Do you really want to ignore ALL exceptions? I would say not! You want 
to ignore a *single* exception, AttributeError. You don't want to hide 
exceptions like NameError (text or doc don't exist) or TypeError (text 
and doc.footer can't be added), because they indicate bugs that should 
not be hidden. Nor do you want to catch exceptions like 
KeyboardInterrupt.

Since the Python compiler can't read the programmer's mind and know what 
exceptions to catch or not to catch, there are only three alternatives:

(1) The current situation: you *must* specify the exception-clause.

(2) Ignore *all* exceptions. This will hide bugs, causing them to crop 
up later when they will be harder to debug. It will also make the 
program un-interruptable by catching KeyboardInterrupt.

(3) Ignore some sub-set of exceptions, like Exception. But again, this 
will still catch exceptions you didn't intend, and possibly let through 
exceptions you wanted to catch.


> The same syntax may be reused in other contexts such as for having
> optional parameters: def f(data, option param):
>    def f(data, ? param):
> Actually, the meaning is the same: "Try & and read a second argument,
> but if you step on an exception just let it down." 

First of all, I think that this will allow a style of programming which 
is lazy and undisciplined and that will lead to more bugs, not fewer. 
If param is *really* optional, then you are probably dealing with 
something best written as two different functions:

def f_without_param(data):

def f_with_param(data, param):


But for the sake of the argument, let's accept that we should write the 
code with an optional argument in that way. Now what happens?

Firstly, every time you refer to param, you need to prefix the line 
with '?' because param may or may not exist. This is inconvenient and 
will lead to bugs when you forget to include a '?' at the start of the 
line. Worse, it will lead to more bugs:

def f(data, ? prefix):
    ? data = prefix + dat  # oops a typo
    print data

f("visible", "in")
=> prints "visible"


What is the interaction between "optional" param and namespaces? 
Consider:

def f(? param):
    ? print param

f()  # I expect this will do nothing, swallowing the NameError exception

param = "spam"
f(param)  # I expect this to print "spam"

f()  # What will this do?


The third case is interesting: should f() print "spam" because param 
exists in the global namespace, or should it do nothing because the 
local argument param doesn't exist? Whichever choice you make, it will 
make some people unhappy.



-- 
Steven D'Aprano



More information about the Python-ideas mailing list