I am currently learning the Template Method pattern in python.
I am wondering if there is any way to protect some of the functions from the base class so that the subclass cannot overwrite? As below, the _primitive_operation_3
from the subclass overwrites the same function from the base class.
import abc
class AbstractClass(metaclass=abc.ABCMeta):
"""
Define abstract primitive operations that concrete subclasses define
to implement steps of an algorithm.
Implement a template method defining the skeleton of an algorithm.
The template method calls primitive operations as well as operations
defined in AbstractClass or those of other objects.
"""
def template_method(self):
self._primitive_operation_1()
self._primitive_operation_2()
self._primitive_operation_3()
# Functions must be specified by subclass (i.e., subclass variant)
@abc.abstractmethod
def _primitive_operation_1(self):
pass
# Functions must be specified by subclass (i.e., subclass variant)
@abc.abstractmethod
def _primitive_operation_2(self):
pass
# Functions inherited and not modified by subclass (i.e., subclass invariant)
def _primitive_operation_3(self):
print ('Execute operation #3 from main class')
class ConcreteClass(AbstractClass):
"""
Implement the primitive operations to carry out
subclass-specificsteps of the algorithm.
"""
def _primitive_operation_1(self):
pass
def _primitive_operation_2(self):
pass
# You can still overwrite it if you want
def _primitive_operation_3(self):
print ('Execute operation #3 from subclass')
def main():
concrete_class = ConcreteClass()
concrete_class.template_method()
if __name__ == "__main__":
main()
And if methods from base class cannot be prevented being overwritten, how can I put something in place to give an automatic alert/warning indicating that a particular method from the base class has been overwritten?
You can't prevent subclasses from using the same names, no. You can protect names against accidental shadowing, however, by giving the name a double underscore prefix:
def __primitive_operation_3(self):
print('Execute operation #3 from main class')
The Python compiler will replace all references to that name, within methods of a class, to add the classname as a prefix. Here, that's AbstractClass
, so the actual name becomes _AbstractClass__primitive_operation_3
, but you because the compiler rewrites all references, you transparently keep using __primitive_operation_3
in your code.
Any __primitive_operation_3
names on a subclass would get renamed with a different prefix, simply because they are defined on a class with a different name.
This feature is explicitly aimed at base classes that want to allow subclasses to use a wide range of names in their definitions.
See the Reserved classes of identifiers section in the lexical analysis reference documentation:
__*
Class-private names. Names in this category, when used within the context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes.
and the Identifiers section of the expressions documentation:
Private name mangling: When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class. Private names are transformed to a longer form before code is generated for them. The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name. For example, the identifier
__spam
occurring in a class namedHam
will be transformed to_Ham__spam
. This transformation is independent of the syntactical context in which the identifier is used. If the transformed name is extremely long (longer than 255 characters), implementation defined truncation may happen. If the class name consists only of underscores, no transformation is done.
Subclasses can still override the name, but have to explicitly include the same prefix.
Note that you can't use this mechanism to avoid special methods (with leading and trailing __
double underscores, e.g. __init__
or __len__
) from being overridden in a subclass. Clear project documentation is paramount if subclasses of your base class can't override specific methods without taking care to call the base implementation. At best you can detect if a subclass is overriding a method by checking for missing side effects (which is how the standard library has protected Thread.__init__
overriding or you can check if self.methodname.__func__ is ClassObject.methodname
is true still before calling the method.