In my experience design-by-contract is worth doing, even without language support. For methods that aren't overridden assertions, along with docstrings are sufficient for both pre- and postconditions. For methods that are overridden we split the method in two: a public method which check the pre- and post-conditions, and a protected method which provide the implementation, and may be overridden by subclasses. Here an example of the latter:
class Math:
def square_root(self, number)
"""
Calculate the square-root of C{number}
@precondition: C{number >= 0}
@postcondition: C{abs(result * result - number) < 0.01}
"""
assert number >= 0
result = self._square_root(number)
assert abs(result * result - number) < 0.01
return result
def _square_root(self, number):
"""
Abstract method for implementing L{square_root()}
"""
raise NotImplementedError()
I got the square root as a general example of design-by-contract from an episode on design-by-contract on software-engineering radio (http://www.se-radio.net/2007/03/episode-51-design-by-contract/). They also mentioned the need for language support because assertions weren't helpful in ensuring the Liskov-substitution-principle, though my example above aims to demonstrate otherwise. I should also mention the C++ pimpl (private implementation) idiom as a source of inspiration, though that has an entirely different purpose.
In my work, I recently refactored this kind of contract-checking into a larger class hierarchy (the contract was already documented, but not systematically tested). Existing unit-tests revealed that the contracts were violated multiple times. I can only conclude this should have been done a long time ago, and that unit-test coverage pays off even more once design-by-contract is applied. I expect anyone who tries out this combination of techniques to make the same observations.
Better tool-support may offer us even more power in the future, I welcome that.