Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What's the use of the else in try/except/else?

3 views
Skip to first unread message

kj

unread,
May 10, 2009, 5:00:23 PM5/10/09
to

I know about the construct:

try:
# do something
except ...:
# handle exception
else:
# do something else

...but I can't come with an example in which the same couldn't be
accomplished with

try:
# do something
# do something else
except ...:
# handle exception

The only significant difference I can come up with is that in the
second form, the except clause may be masking some unexpected
exceptions from the "do something else" part. Is this the rationale
behind this else clause? Or is there something more to it?

TIA!

kynn

--
NOTE: In my address everything before the first period is backwards;
and the last period, and everything after it, should be discarded.

Scott David Daniels

unread,
May 10, 2009, 6:52:23 PM5/10/09
to
kj wrote:
> ... I can't come with an example in which the same couldn't be

> accomplished with
>
> try:
> # do something
> # do something else
> except ...:
> # handle exception
>
> The only significant difference I can come up with is that in the
> second form, the except clause may be masking some unexpected
> exceptions from the "do something else" part. Is this the rationale
> behind this else clause? Or is there something more to it?

Yes, in a way. The idea of catching particular exceptions is to only
handle exceptions you expect (let the others go out to more general
reporters). So, not only should you choose the tightest exception to
catch that you can, but you should look for it in a very narrow window:
exactly where you expect it.

try:
v = mumble.field
except AttributeError:
pass
else:
sys.warning('field was actually there?')

as opposed to:

try:
v = mumble.field
sys.warning('field was actually there?')
except AttributeError:
pass

The idea is to make it clear what you expect might go
wrong that you are prepared to handle.

--Scott David Daniels
Scott....@Acm.Org

kj

unread,
May 11, 2009, 11:19:05 AM5/11/09
to

>as opposed to:

Wow. As rationales for syntax constructs go, this has got to be
the most subtle one I've ever seen...

Thanks!

Lawrence D'Oliveiro

unread,
May 12, 2009, 4:23:25 AM5/12/09
to
In message <gu7f97$mt6$2...@reader1.panix.com>, kj wrote:

> I know about the construct:
>
> try:
> # do something
> except ...:
> # handle exception
> else:
> # do something else
>
> ...but I can't come with an example in which the same couldn't be

> accomplished with [no else]

I'd agree. If you have to resort to a "try .. else", then might I
respectfully suggest that you're using exceptions in a way that's
complicated enough to get you into trouble.

Steven D'Aprano

unread,
May 12, 2009, 5:20:36 AM5/12/09
to

try:
rsrc = get(resource)
except ResourceError:
log('no more resources available')
raise
else:
do_something_with(rsrc)
finally:
rsrc.close()

is complicated? It seems pretty straightforward to me.

--
Steven

Steven D'Aprano

unread,
May 12, 2009, 5:35:36 AM5/12/09
to

Except of course such a pattern won't work, because if get(resource)
fails, rsrc will not exist to be closed. So a better, and simpler,
example would be to drop the finally clause:

try:
rsrc = get(resource)
except ResourceError:
log('no more resources available')
raise
else:
do_something_with(rsrc)

rsrc.close()


To really be safe, that should become:

try:
rsrc = get(resource)
except ResourceError:
log('no more resources available')
raise
else:

try:
do_something_with(rsrc)
finally:
rsrc.close()


which is now starting to get a bit icky (but only a bit, and only because
of the nesting, not because of the else).

--
Steven

Peter Pearson

unread,
May 12, 2009, 11:45:09 AM5/12/09
to
On 12 May 2009 09:35:36 GMT, Steven D'Aprano wrote:
[snip]

> To really be safe, that should become:
>
> try:
> rsrc = get(resource)
> except ResourceError:
> log('no more resources available')
> raise
> else:
> try:
> do_something_with(rsrc)
> finally:
> rsrc.close()

Thanks, Steven. I find these examples illuminating.

Not trying to be dense, but given that the "except" block
re-raises the exception, isn't the above the same as . . .?

try:
rsrc = get(resource)
except ResourceError:
log('no more resources available')
raise

try:
do_something_with(rsrc)
finally:
rsrc.close()

--
To email me, substitute nowhere->spamcop, invalid->net.

Carl Banks

unread,
May 12, 2009, 5:04:41 PM5/12/09
to
On May 12, 2:35 am, Steven D'Aprano

<ste...@REMOVE.THIS.cybersource.com.au> wrote:
> On Tue, 12 May 2009 09:20:36 +0000, Steven D'Aprano wrote:
> > On Tue, 12 May 2009 20:23:25 +1200, Lawrence D'Oliveiro wrote:
>

Resource acquisition goes outside the try block. If you want to log
an error, then get() goes inside its own try block.

try:
rsrc = get(resource)
except ResourceError:
log('no more resources available')
raise

try:
do_something_with(rsrc)
finally:
rsrc.close()


If you hadn't reraised the exception, then the else clause would have
been useful as follows:

try:
rsrc = get(resource)
except ResourceError:

proceed_without_resource()
else:
try:
proceed_with_resource()
# Note: proceed_with_resource() can possibly raise
# ResourceError itself
finally:
rsrc.close()

The main reason for the else clause on try blocks is so that you don't
risk catching spurrious exceptions. You could stick
proceed_with_resource() in the try clause, but then if
proceed_with_resource() throws ResourceError because it tries to
acquire a different resource and fails, then it'd be caught and
proceed_without_resource() would be called, which is a mistake.

In general, you want to limit the contents of a try clause to code
that throws an exception you want to catch (if you're trying to catch
an exception); everything else should go into the else clause.

Incidentally, I can't think of any realistic use cases for using all
four of try...except...else...finally.


Carl Banks

greg

unread,
May 13, 2009, 12:20:25 AM5/13/09
to
kj wrote:

> Wow. As rationales for syntax constructs go, this has got to be
> the most subtle one I've ever seen...

It's to avoid masking bugs. Suppose you accidentally
wrote

try:
v = mumble.field
sys.warming('field was actually there?')
except AttributeError:
pass

Then you could easily fail to notice that
you had written 'warming' instead of 'warning'.

--
Greg

Lawrence D'Oliveiro

unread,
May 13, 2009, 4:44:27 AM5/13/09
to
In message <pan.2009.05...@REMOVE.THIS.cybersource.com.au>, Steven
D'Aprano wrote:

> On Tue, 12 May 2009 09:20:36 +0000, Steven D'Aprano wrote:
>

>> It seems pretty straightforward to me.
>

> Except of course such a pattern won't work ...

I rest my case.

Scott David Daniels

unread,
May 13, 2009, 1:53:26 PM5/13/09
to
Well, if you look, you'll discover that warning is not in the
sys module at all, but in the logging module.
So the original example already demonstrated the problem.

--Scott David Daniels
Scott....@Acm.Org

Steven D'Aprano

unread,
May 13, 2009, 8:40:14 PM5/13/09
to

Gosh, with such shallowness of analysis and out-of-context quoting, how
could I possibly disagree? You've convinced me utterly.

--
Steven

ma

unread,
May 14, 2009, 12:39:35 AM5/14/09
to kj...@lycos.com, pytho...@python.org
A really great use for try/except/else would be if an object is
implementing its own __getitem__ method, so you would have something
like this:

class SomeObj(object):
def __getitem__(self, key):
try:
#sometype of assertion here based on key type
except AssertionError, e:
raise TypeError, e #invalid type
else:
#continue processing, etc.. return some index, which will auto throw
#an index error if you have some type of indexable datastructure

Steven D'Aprano

unread,
May 14, 2009, 1:03:16 AM5/14/09
to
On Thu, 14 May 2009 00:39:35 -0400, ma wrote:

> A really great use for try/except/else would be if an object is
> implementing its own __getitem__ method, so you would have something
> like this:
>
> class SomeObj(object):
> def __getitem__(self, key):
> try:
> #sometype of assertion here based on key type
> except AssertionError, e:
> raise TypeError, e #invalid type

Why raise AssertionError only to catch it and raise TypeError? Why not
just raise TypeError in the first place?


If you're thinking of writing this:

assert isinstance(key, whatever)

you should be aware that when Python is run with the -O flag (optimize),
all asserts are disabled, and so your error checking code will not run
and your program will crash and burn in a horrible flaming mess.


[steve@wow-wow ~]$ python -O
Python 2.5 (r25:51908, Nov 6 2007, 16:54:01)
[GCC 4.1.2 20070925 (Red Hat 4.1.2-27)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> assert None is 2
>>>
>>>


The assert statement is not for runtime error checking. assert is for
checking your program logic and invariants. If you've ever written a
comment like:

# When we get here, then x will always be greater than 3.

(or something like that), that's a great candidate for an assertion:

assert x > 3, "x is unexpectedly less than or equal to three"


For error checking, you should do something like this:

if not isinstance(key, whatever):
raise ValueError

rather than:

try:
if not isinstance(key, whatever):
raise AssertionError
except AssertionError:
raise ValueError


--
Steven

ma

unread,
May 14, 2009, 1:19:50 AM5/14/09
to Steven D'Aprano, pytho...@python.org
That's great to know! Thanks for that explanation, I am refactoring
something and I was going to make ample use of assertion as I thought
it was the same as C's assertion without the NDEBUG flag.

> --
> http://mail.python.org/mailman/listinfo/python-list
>

Andre Engels

unread,
May 14, 2009, 3:08:24 AM5/14/09
to ma, pytho...@python.org, kj...@lycos.com
On Thu, May 14, 2009 at 6:39 AM, ma <mabde...@gmail.com> wrote:
> A really great use for try/except/else would be if an object is
> implementing its own __getitem__ method, so you would have something
> like this:
>
> class SomeObj(object):
>    def __getitem__(self, key):
>                try:
>                        #sometype of assertion here based on key type
>                except AssertionError, e:
>                        raise TypeError, e #invalid type
>                else:
>                        #continue processing, etc.. return some index, which will auto throw
>                        #an index error if you have some type of indexable datastructure

Again, this is a case where no else is needed. Whenever you raise
something in the except clause (as your only exit point), it would
work just as well (though it might be a bit uglier) to put the rest
after the try ... except without an else. It is when after the
exception execution continues normally, but skipping part of the code,
that you need an else.


--
André Engels, andre...@gmail.com

Beni Cherniavsky

unread,
May 17, 2009, 10:26:04 AM5/17/09
to
[Long mail. You may skip to the last paragraph to get the summary.]

On May 12, 12:35 pm, Steven D'Aprano wrote:
> To really be safe, that should become:
>
> try:
>     rsrc = get(resource)
> except ResourceError:
>     log('no more resources available')
>     raise
> else:
>     try:
>         do_something_with(rsrc)
>     finally:
>         rsrc.close()
>
> which is now starting to get a bit icky (but only a bit, and only because
> of the nesting, not because of the else).
>

Note that this example doesn't need ``else``, because the ``except``
clause re-raises the exception. It could as well be::

try:
rsrc = get(resource)
except ResourceError:
log('no more resources available')
raise

try:
do_something_with(rsrc)
finally:
rsrc.close()

``else`` is relevant only if your ``except`` clause(s) may quietly
suppress the exception::

try:
rsrc = get(resource)
except ResourceError:

log('no more resources available, skipping do_something')


else:
try:
do_something_with(rsrc)
finally:
rsrc.close()

And yes, it's icky - not because of the ``else`` but because
aquisition-release done correctly is always an icky pattern. That's
why we now have the ``with`` statement - assuming `get()` implements a
context manager, you should be able to write::

with get(resource) as rsrc:
do_something_with(rsrc)

But wait, what if get() fails? We get an exception! We wanted to
suppress it::

try:
with get(resource) as rsrc:
do_something_with(rsrc)
except ResourceError:
log('no more resources available, skipping do_something')

But wait, that catches ResourceError in ``do_something_with(rsrc)`` as
well! Which is precisely what we tried to avoid by using
``try..else``!
Sadly, ``with`` doesn't have an else clause. If somebody really
believes it should support this pattern, feel free to write a PEP.

I think this is a bad example of ``try..else``. First, why would you
silently suppress out-of-resource exceptions? If you don't suppress
them, you don't need ``else``. Second, such runtime problems are
normally handled uniformely at some high level (log / abort / show a
message box / etc.), wherever they occur - if ``do_something_with(rsrc)
`` raises `ResourceError` you'd want it handled the same way.

So here is another, more practical example of ``try..else``:

try:
bar = foo.get_bar()
except AttributeError:
quux = foo.get_quux()
else:
quux = bar.get_quux()

assuming ``foo.get_bar()`` is optional but ``bar.get_quux()`` isn't.
If we had put ``bar.get_quux()`` inside the ``try``, it could mask a
bug. In fact to be precise, we don't want to catch an AttributeError
that may happen during the call to ``get_bar()``, so we should move
the call into the ``else``::

try:
get_bar = foo.get_bar
except AttributeError:
quux = foo.get_quux()
else:
quux = get_bar().get_quux()

Ick!

The astute reader will notice that cases where it's important to
localize exception catching involves frequent excetions like
`AttributeError` or `IndexError` -- and that these cases are already
handled by `getattr` and `dict.get` (courtesy of Guido's Time
Machine).

Bottom line(s):
1. ``try..except..else`` is syntactically needed only when ``except``
might suppress the exception.
2. Minimal scope of ``try..except`` doesn't always apply (for
`AttirbuteError` it probably does, for `MemoryError` it probably
doesn't).
3. It *is* somewhat ackward to use, which is why the important use
cases - exceptions that are frequently raised and caught - deserve
wrapping by functions like `getattr()` with default arguments.

Lawrence D'Oliveiro

unread,
May 22, 2009, 10:18:21 PM5/22/09
to
In message <8dc983db-b8c4-4897-

> And yes, it's icky - not because of the ``else`` but because
> aquisition-release done correctly is always an icky pattern.

Only in the presence of exceptions.

0 new messages