- So can assertions be useful even in code that is well unit-tested?
- Are there any rules we can use to choose which things might be more productively tested using such asserts, rather than in unit tests?
To answer the latter question I prefer to paraphrase this wiki article that states one should consider using assertions in the following cases:
- checking parameter types, classes, or values
- checking data structure invariants
- checking "can't happen" situations (duplicates in a list, contradictory state variables.)
- after calling a function, to make sure that its return is reasonable
At the simplest level you can check type using isinstance. On the long run checking types this way may get just plain boring. Alternatively it's possible to use decorator or docstring based solutions. I haven't found any actual example for the latter case but it might be possible to get inspired by Contracts for Python. Besides parameter types it should be easy to assert the return type as well.
So how to gain the performance benefits I hinted about before? Simply use Pyrex or Cython. The basic idea is that you provide some extra information in your code based on which either of these solutions generates a high performance C extension. To get started, check out this tutorial to Cython.