Python class
es are not self-contained entities, they are a grouping of separate entities – mostly methods but also properties, etc. – and some metadata – such as the class name and base classes. Notably, this group only acts like what is commonly understood as being a class when used; the group itself is inert and will not eagerly check for consistency or correctness.
In specific, methods on the class are just functions. Like any function, methods do not eagerly check whether their dependencies exist at definition time.
def say_hello():
print('Hello')
say_byebye() # say_byebye does not exist at definition time
def say_byebye():
print('ByeBye')
say_hello() # works, say_byebye exists at usage time
Similarly, a method does not eagerly check whether self
(or any other parameter) has the methods/attributes required by the method. This makes it fine to define a method in an incomplete or abstract class but use the method in a derived, complete class.
In general, classes should be self-contained and functional – relying on subclasses or multiple-inheritance for completeness is usually not good practice, since it is not obvious from the class itself. However, the pattern of mixins uses this style and naming a class as Mixin<XYZ>
will generally convey that (multiple-) inheritance is required for functionality.
To ensure that partial classes are not misused and obviously indicate what parts are missing, it is advisable to define them as abstract base classes. A weak form merely defines methods that raise NotImplementedError
, while the strong form use the abc
module to enforce contracts.
from abc import ABCMeta, abstractmethod
class A(metaclass=ABCMeta):
def say_hello(self):
print('Hello')
self.say_byebye()
# an `abstractmethod` must be overwritten to instantiate an ABCMeta class
@abstractmethod
def say_byebye(self):
raise NotImplementedError