I am just now learning Python, and I came across an interesting construct. In Python, the try
block has an optional else
block. Other than providing a new scope after the try exits normally, what does the else
block do for you? Or is that just it?
views:
2381answers:
9The statements in the else
block are executed if execution falls off the bottom of the try
- if there was no exception. Honestly, I've never found a need.
However, Handling Exceptions notes:
The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try ... except statement.
So, if you have a method that could, for example, throw an IOError
, and you want to catch exceptions it raises, but there's something else you want to do if the first operation succeeds, and you don't want to catch an IOError from that operation, you might write something like this:
try:
operation_that_can_throw_ioerror()
except IOError:
handle_the_exception_somehow()
else:
# we don't want to catch the IOError if it's raised
another_operation_that_can_throw_ioerror()
finally:
something_we_always_need_to_do()
If you just put another_operation_that_can_throw_ioerror()
after operation_that_can_throw_ioerror
, the except
would catch the second call's errors. And if you put it after the whole try
block, it'll always be run, and not until after the finally
. The else
lets you make sure
- the second operation's only run if there's no exception,
- it's run before the
finally
block, and - any
IOError
s it raises aren't caught here
Never used it myself. Looking at Python reference it seems that else
is executed after try
when there's no exception.
The optional else clause is executed if and when control flows off the end of the try clause. [2] Exceptions in the else clause are not handled by the preceding except clauses.
Dive into python has and example where, if I understand correctly, in try
block they try to import a module, when that fails you get exception and bind default but when it works you have an option to go into else
block and bind what is required (see link for the example and explanation).
If you tried to do work in catch
block it might throw another exception - I guess that's where the else
block comes handy.
Even though you can't think of a use of it right now, you can bet there has to be a use for it. Here is an unimaginative sample out of my head:
a = [1,2,3]
try:
something = a[2]
except:
print "out of bounds"
else:
print something
try:
something = a[2]
except:
print "out of bounds"
if "something" in locals():
print something
Here you have the variable "something" defined if no error is thrown. You can remove this outside the try block, but then it requires some messy detection if a variable is defined.
The else:
block is confusing and (nearly) useless. It's also part of the for
and while
statements.
Actually, even on an if
-statement, the else:
can be abused in truly terrible ways creating bugs that are very hard to find.
Consider this.
if a < 10:
# condition stated explicitly
elif a > 10 and b < 10:
# condition confusing but at least explicit
else:
# Exactly what is true here?
# Can be hard to reason out what condition is true
Think twice about else:
. It is generally a problem. Avoid it except in an if
-statement and even then consider documenting the else
- condition to make it explicit.
I find it really useful when you've got cleanup to do that has to be done even if there's an exception:
try:
data = something_that_can_go_wrong()
except Exception, e: # yes, I know that's a bad way to do it...
handle_exception(e)
else:
do_stuff(data)
finally:
clean_up()
One use: test some code that should raise an exception.
try:
this_should_raise_TypeError()
except TypeError:
pass
except:
assert False, "Raised the wrong exception type"
else:
assert False, "Didn't raise any exception"
(This code should be abstracted into a more generic test in practice.)
That's it. The 'else' block of a try-except clause exists for code that runs when (and only when) the tried operation succeeds. It can be used, and it can be abused.
try:
fp= open("configuration_file", "rb")
except EnvironmentError:
confdata= '' # it's ok if the file can't be opened
else:
confdata= fp.read()
fp.close()
# your code continues here
# working with (possibly empty) confdata
Personally, I like it and use it when appropriate. It semantically groups statements.
An else
block can often exist to complement functionality that occurs in every except
block.
try:
test_consistency(valuable_data)
except Except1:
inconsistency_type = 1
except Except2:
inconsistency_type = 2
except:
# Something else is wrong
raise
else:
inconsistency_type = 0
"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""
In this case, inconsistency_type
is set in each except block, so that behaviour is complemented in the no-error case in else
.
Of course, I'm describing this as a pattern that may turn up in your own code someday. In this specific case, you just set inconsistency_type
to 0 before the try
block anyway.
There's a nice example of try-else
in PEP 380. Basically, it comes down to doing different exception handling in different parts of the algorithm.
It's something like this:
try:
do_init_stuff()
except:
handle_init_suff_execption()
else:
try:
do_middle_stuff()
except:
handle_middle_stuff_exception()
This allows you to write the exception handling code nearer to where the exception occurs.