Today I will show you a quick way of forcing any subclass of a superclass to define required attributes. I was recently writing a software for my PhD. I was developing a framework that could be useful for other programmers in the future. However, this framework is quite generic and is not implementing any case-specific or robot-specific behaviours. Therefore, I am expecting from the programmers using this framework to specify the required attributes that are too specific to go in the superclass.
I found a nice way of throwing an error if any subclass is not defining
the required attributes at object instantiation level (i.e during object instantiation).
This way, we can make sure that the attributes are defined and we can get rid of
some conditional statements from our code (i.e
if self.x is not None:).
Have a quick look at the code below:
As you can see, at line 23 we are using the
__metaclass__ (Python 2) or line 31
metaclass= (Python 3) in
to define a metaclass (the
ForceRequiredAttributeDefinitionMeta) so Python will use this
metaclass to create the class.
In the metaclass, we override the magic method
__call__. As you can see from
Figure 1, first the
__call__ method is called when creating a class instance
which in turn is calling the class’s
__init__ before returning
the instance to the caller. So this interferes with the creation of class’s instances. This is exactly
what we want. We would like after
__init__ of the instance to call a method to
check if required attributes have been defined. This is done on line 18 of the metaclass.
ForceRequiredAttributeDefinition has to implements the
method which in turn has to check if the ‘required’ attributes actually exist (Note that we
define the required attributes as
None first). In the case where the required
None, we are raising a
NotImplementedError with an appropriate message
to let the user know about the required attribute.
You can also see a demonstration using the subclass
defines the required attribute
starting_day_of_week and another subclass,
which does not define the required attribute
starting_day_of_week and throws an
NotImplementedError at instantiation level.
Here is the output of running the script above:
Traceback (most recent call last): File "test.py", line 50, in <module> ConcereteInvalidExample() # This will throw an NotImplementedError straightaway File "test.py", line 18, in __call__ obj.check_required_attributes() File "test.py", line 36, in check_required_attributes raise NotImplementedError('Subclass must define self.starting_day_of_week attribute. \n This attribute should define the first day of the week.') NotImplementedError: Subclass must define self.starting_day_of_week attribute. This attribute should define the first day of the week.
As you can see the
ConcereteValidExample was successful but the
This may not be the “Pythonic” way of solving the problem, however, it just works. With all that said, I am still looking for a nicer way of solving this particular problem. If I do find something I will let you know.