Modern recommended exception handling practices?

Chris Angelico rosuav at gmail.com
Tue Nov 3 02:52:27 EST 2015


On Tue, Nov 3, 2015 at 6:22 PM, Steven D'Aprano <steve at pearwood.info> wrote:
> A lot can happen in the few microseconds between
> checking for the existence of the file and actually opening it -- the file
> could be renamed or deleted.

And a lot of microseconds can happen between two opcodes, too. Even
inside a Python script, it's possible for threads or other arbitrary
code execution to get in your way:

if "foo" in counters:
    # context switch here!
    process(counters["foo"])

Garbage collection can happen at any time. Here's an (admittedly
arbitrary) example of how the above could be broken:

>>> class X:
...     def __init__(self, name, dict):
...         self.dict = dict; self.name = name
...         self.dict[self.name] = 0
...     def frob(self):
...         self.dict[self.name] += 1
...     def __del__(self):
...         del self.dict[self.name]
...
>>> counters = {}
>>> x = X("foo", counters)
>>> x.refcycle = x
>>> counters
{'foo': 0}
>>> del x
>>> counters
{'foo': 0}
>>> gc.collect()
10
>>> counters
{}

If the cycle-detecting garbage collector happens to be called
immediately after the 'if', you'll get an exception.

So I suppose what you might do is this:

try:
    # Optimization: Since a lot of these names won't be
    # in the dict, we check first rather than relying on the
    # exception. Since counters get removed in the __del__
    # method, we can't depend 100% on the 'in' check,
    # but an unnecessary try block is cheap.
    if "foo" in counters:
        process(counters["foo"])
except KeyError:
    pass

But any time you need a block comment to justify your code, you'd
better be REALLY sure the performance benefit is worth the complexity.

For reliability, expect exceptions.

ChrisA



More information about the Python-list mailing list