[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