Python has a conventional object-oriented design, but it was slowly grafted onto the language, something which shows from time to time. Arguably, you see this in the convention that instance methods need self
passed as their first argument, and class methods need cls
as their first argument. Another place you see it is how Python does abstract classes. First, one can use definitions in the built-in abc
module, proposed in PEP-3119, to declare a class as abstract. But in practice most Pythonistas make a class abstract by declaring unimplemented instance methods. There are two conventional ways to do this, either with ellipses or by raising an exception, illustrated below.
class AbstractCandyFactory: def make_candy(self, batch_size: int): ...
class AbstractCandyFactory: def make_candy(self, batch_size: int): raise NotImplementedError
The latter is a bit more verbose, but there is actually a very good reason to prefer it to the former, elliptical version. With the exception version, if one forgets to implement make_candy
—say, in a concrete subclass like SnickersFactory(AbstractCandyFactory)
—an informative exception will be raised when make_candy
is called on a SnickersFactory
instance. However, in the elliptical form, the inherited form will be called, and of course will do nothing because the method has no body. This will likely cause errors down the road, but they will not be nearly as easy to track down because there is nothing to directly link the issue to the failure to override this method. For this reason alone, I consider ellipses used to declare abstract instance methods as harmful.