[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. -- http://mail.python.org/mailman/listinfo/python-list