More Exceptions
Instead of handling all errors, we can just handle the TypeError
we saw above:
def add(self, item): try: self.tree.add(item) except TypeError: pass
This way, we’re only ignoring ~TypeError~s.
We’re still just failing silently if we get multiple types of values, though. We really want to raise an exception–it should just be a more informative exception than the one we see here! We can do something like this:
def add(self, item): try: self.tree.add(item) except TypeError: raise TypeError("Values in a TreeSet must all have the same type")
Now our caller will still get an error message, but it will be much more helpful!
Testing exceptions
If your code raises exceptions (either via your own raise
statements or, for
instance, because it might divide by zero), you should test those error
cases. (In my experience, error-handling code is frequently riddled with
bugs–it doesn’t run in normal operations, so bugs don’t get noticed and fixed!
Always test your error-handling code.) pytest
lets you do this:
from tree_set import * import pytest def test_add(): s = TreeSet() s.add(1) with pytest.raises(TypeError, match="same type"): s.add("1")
Exceptions and the “call stack”
Let’s say we have a few functions defined:
def divide(x, y): return x / y def average(lst): return divide(sum(lst), len(lst)) def variance(lst): try: avg = average(lst) avg_2 = average([x * x for x in lst]) return abs(avg * avg - avg_2) except ZeroDivisionError: return 0
What happens when we call variance([])
? See the lecture capture for details.
Polymorphism example: Python’s special methods
With Python’s built-in sets, lists, and hashtables, we can check if an element
is in the collection with in
:
l = [1, 2, 3] 2 in l # => True 4 in l # => False
With our TreeSet
, though, we have to use the contains
method. This doesn’t
seem fair!
Luckily, Python’s in
is actually implemented by calling a special method on
the container object. We can add an implementation like this:
# on the TreeSet class def __contains__(self, item): return self.contains(item)
Now we can do this:
s = TreeSet() s.add(1) s.add(2) 1 in s # => True 3 in s # => False
Python uses this kind of special method for a lot of things. Even things like
addition using the `+` operator are actually implemented using these special
methods. This is a classic example of polymorphism. Python doesn’t need to
know anything about how a TreeSet
is implemented: it just has to call its
`_contains__` method.