What is the intended use of the optional else
clause of the try
statement?
问题:
回答1:
The 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
回答2:
There is one big reason to use else
- style and readability. It\'s generally a good idea to keep code that can cause exceptions near the code that deals with them. For example, compare these:
try:
from EasyDialogs import AskPassword
# 20 other lines
getpass = AskPassword
except ImportError:
getpass = default_getpass
and
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
else:
# 20 other lines
getpass = AskPassword
The second one is good when the except
can\'t return early, or re-throw the exception. If possible, I would have written:
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
return False # or throw Exception(\'something more descriptive\')
# 20 other lines
getpass = AskPassword
Note: Answer copied from recently-posted duplicate here, hence all this \"AskPassword\" stuff.
回答3:
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.)
回答4:
Python try-else
What is the intended use of the optional
else
clause of the try statement?
Summary
The else
statement runs if there are no exceptions and if not interrupted by a return
, continue
, or break
statement.
The other answers miss that last part.
From the docs:
The optional
else
clause is executed if and when control flows off the end of thetry
clause.*
(Bolding added.) And the footnote reads:
*Currently, control “flows off the end” except in the case of an exception or the execution of a
return
,continue
, orbreak
statement.
It does require at least one preceding except clause (see the grammar). So it really isn\'t \"try-else,\" it\'s \"try-except-else(-finally),\" with the else
(and finally
) being optional.
The Python Tutorial elaborates on the intended usage:
The try ... except statement has an optional else clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:
for arg in sys.argv[1:]: try: f = open(arg, \'r\') except IOError: print \'cannot open\', arg else: print arg, \'has\', len(f.readlines()), \'lines\' f.close()
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.
Example differentiating else
versus code following the try
block
If you handle an error, the else
block will not run. For example:
def handle_error():
try:
raise RuntimeError(\'oops!\')
except RuntimeError as error:
print(\'handled a RuntimeError, no big deal.\')
else:
print(\'if this prints, we had no error!\') # won\'t print!
print(\'And now we have left the try block!\') # will print!
And now,
>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!
回答5:
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 as e: # yes, I know that\'s a bad way to do it...
handle_exception(e)
else:
do_stuff(data)
finally:
clean_up()
回答6:
Try-except-else is great for combining the EAFP pattern with duck-typing:
try:
cs = x.cleanupSet
except AttributeError:
pass
else:
for v in cs:
v.cleanup()
You might thing this naïve code is fine:
try:
for v in x.cleanupSet:
v.clenaup()
except AttributeError:
pass
This is a great way of accidentally hiding severe bugs in your code. I typo-ed cleanup there, but the AttributeError that would let me know is being swallowed. Worse, what if I\'d written it correctly, but the cleanup method was occasionally being passed a user type that had a misnamed attribute, causing it to silently fail half-way through and leave a file unclosed? Good luck debugging that one.
回答7:
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:
With else
:
a = [1,2,3]
try:
something = a[2]
except:
print \"out of bounds\"
else:
print something
Without else
:
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.
回答8:
From Errors and Exceptions # Handling exceptions - docs.python.org
The
try ... except
statement has an optionalelse
clause, which, when present, must follow all except clauses. It is useful for code that must be executed if the try clause does not raise an exception. For example:for arg in sys.argv[1:]: try: f = open(arg, \'r\') except IOError: print \'cannot open\', arg else: print arg, \'has\', len(f.readlines()), \'lines\' f.close()
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.
回答9:
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.
回答10:
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 an 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.
回答11:
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.
回答12:
Most answers seem to concentrate on why we can\'t just put the material in the else clause in the try clause itself. The question else clause in try statement... what is it good for specifically asks why the else clause code cannot go after the try block itself, and that question is dupped to this one, but I do not see a clear reply to that question here. I feel https://stackoverflow.com/a/3996378/1503120 excellently answers that question. I have also tried to elucidate the various significance of the various clauses at https://stackoverflow.com/a/22579805/1503120.
回答13:
Perhaps a use might be:
#debug = []
def debuglog(text, obj=None):
\" Simple little logger. \"
try:
debug # does global exist?
except NameError:
pass # if not, don\'t even bother displaying
except:
print(\'Unknown cause. Debug debuglog().\')
else:
# debug does exist.
# Now test if you want to log this debug message
# from caller \"obj\"
try:
if obj in debug:
print(text) # stdout
except TypeError:
print(\'The global \"debug\" flag should be an iterable.\')
except:
print(\'Unknown cause. Debug debuglog().\')
def myfunc():
debuglog(\'Made it to myfunc()\', myfunc)
debug = [myfunc,]
myfunc()
Maybe this will lead you too a use.
回答14:
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.
回答15:
I have found the try: ... else:
construct useful in the situation where you are running database queries and logging the results of those queries to a separate database of the same flavour/type. Let\'s say I have lots of worker threads all handling database queries submitted to a queue
#in a long running loop
try:
query = queue.get()
conn = connect_to_db(<main db>)
curs = conn.cursor()
try:
curs.execute(\"<some query on user input that may fail even if sanitized\">)
except DBError:
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute(\"<update in DB log with record of failed query\")
logcurs.close()
logconn.close()
else:
#we can\'t put this in main try block because an error connecting
#to the logging DB would be indistinguishable from an error in
#the mainquery
#We can\'t put this after the whole try: except: finally: block
#because then we don\'t know if the query was successful or not
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute(\"<update in DB log with record of successful query\")
logcurs.close()
logconn.close()
#do something in response to successful query
except DBError:
#This DBError is because of a problem with the logging database, but
#we can\'t let that crash the whole thread over what might be a
#temporary network glitch
finally:
curs.close()
conn.close()
#other cleanup if necessary like telling the queue the task is finished
Of course if you can distinguish between the possible exceptions that might be thrown, you don\'t have to use this, but if code reacting to a successful piece of code might throw the same exception as the successful piece, and you can\'t just let the second possible exception go, or return immediately on success (which would kill the thread in my case), then this does come in handy.
回答16:
Here is another place where I like to use this pattern:
while data in items:
try
data = json.loads(data)
except ValueError as e:
log error
else:
# work on the `data`
回答17:
Suppose your programming logic depends on whether a dictionary has an entry with a given key. You can test the result of dict.get(key)
using if... else...
construct, or you can do:
try:
val = dic[key]
except KeyError:
do_some_stuff()
else:
do_some_stuff_with_val()
回答18:
One of the use scenarios I can think of is unpredictable exceptions, which can be circumvented if you try again. For instance, when the operations in try block involves random numbers:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
But if the exception can be predicted, you should always choose validation beforehand over an exception. However, not everything can be predicted, so this code pattern has its place.
回答19:
I have found else
useful for dealing with a possibly incorrect config file:
try:
value, unit = cfg[\'lock\'].split()
except ValueError:
msg = \'lock monitoring config must consist of two words separated by white space\'
self.log(\'warn\', msg)
else:
# get on with lock monitoring if config is ok
An exception reading the lock
config disables lock monitoring and ValueErrors log a helpful warning message.
回答20:
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.