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

Discussion in 'Python' started by kj, May 10, 2009.

  1. kj

    kj Guest

    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.
    kj, May 10, 2009
    #1
    1. Advertising

  2. kj

    kj Guest

    In <> Scott David Daniels <> writes:

    >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.


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

    Thanks!

    kynn

    --
    NOTE: In my address everything before the first period is backwards;
    and the last period, and everything after it, should be discarded.
    kj, May 11, 2009
    #2
    1. Advertising

  3. In message <gu7f97$mt6$>, 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.
    Lawrence D'Oliveiro, May 12, 2009
    #3
  4. On Tue, 12 May 2009 20:23:25 +1200, Lawrence D'Oliveiro wrote:

    > In message <gu7f97$mt6$>, 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.




    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, May 12, 2009
    #4
  5. 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:
    >
    >> In message <gu7f97$mt6$>, 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.

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




    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
    Steven D'Aprano, May 12, 2009
    #5
  6. 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.
    Peter Pearson, May 12, 2009
    #6
  7. kj

    Carl Banks Guest

    On May 12, 2:35 am, Steven D'Aprano
    <> 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:

    >
    > >> In message <gu7f97$>, 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.

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

    >
    > 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:


    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
    Carl Banks, May 12, 2009
    #7
  8. kj

    greg Guest

    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
    greg, May 13, 2009
    #8
  9. In message <>, 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.
    Lawrence D'Oliveiro, May 13, 2009
    #9
  10. On Wed, 13 May 2009 20:44:27 +1200, Lawrence D'Oliveiro wrote:

    > In message <>,
    > 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.


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



    --
    Steven
    Steven D'Aprano, May 14, 2009
    #10
  11. kj

    ma Guest

    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
    ma, May 14, 2009
    #11
  12. 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
    Steven D'Aprano, May 14, 2009
    #12
  13. kj

    ma Guest

    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.


    On Thu, May 14, 2009 at 1:03 AM, Steven D'Aprano
    <> wrote:
    > 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
    > --
    > http://mail.python.org/mailman/listinfo/python-list
    >
    ma, May 14, 2009
    #13
  14. kj

    Andre Engels Guest

    On Thu, May 14, 2009 at 6:39 AM, 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
    >                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 Engels, May 14, 2009
    #14
  15. [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.
    Beni Cherniavsky, May 17, 2009
    #15
  16. In message <8dc983db-b8c4-4897-
    >, Beni Cherniavsky wrote:

    > 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.
    Lawrence D'Oliveiro, May 23, 2009
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Salerno
    Replies:
    4
    Views:
    396
    John Salerno
    Jul 28, 2006
  2. John Salerno
    Replies:
    20
    Views:
    843
    John Salerno
    Aug 11, 2006
  3. Fabio Z Tessitore

    who is simpler? try/except/else or try/except

    Fabio Z Tessitore, Aug 12, 2007, in forum: Python
    Replies:
    5
    Views:
    367
  4. David House

    try -> except -> else -> except?

    David House, Jul 6, 2009, in forum: Python
    Replies:
    2
    Views:
    332
    Bruno Desthuilliers
    Jul 6, 2009
  5. MRAB
    Replies:
    4
    Views:
    306
Loading...

Share This Page