debugpythonMinor
Raising error if method not overridden by sub-class
Viewed 0 times
errorclassmethodraisingsubnotoverridden
Problem
Background
I have a base (only 2 classes inherit from it, and only from it) abstract (meaning I don't want it to be used directly) class that implements some common functionality.
Some of it depends on certain methods being defined, methods whose implementation differs per the exact sub-class; so I set-up a mechanism to raise an exception in case that kind of thing happened (better explicit fail than subtle debugging, right?).
Code
Question
Is that an anti-pattern, like singletons or w/e? If so, what's a better way? Should I even check for this at all (i.e., maybe similar to excessive type-checking)?
Disclaimer: Python was my first language, and I do NOT like Java (although I have some experience from Android development); so this is not me trying to port some ruddy static pattern from another language.
I have a base (only 2 classes inherit from it, and only from it) abstract (meaning I don't want it to be used directly) class that implements some common functionality.
Some of it depends on certain methods being defined, methods whose implementation differs per the exact sub-class; so I set-up a mechanism to raise an exception in case that kind of thing happened (better explicit fail than subtle debugging, right?).
Code
class Matrix:
"""Model an abstract base interface for 2-d matrices."""
def __init__(self):
"""Raise error as class is abstract."""
self.__eq__ = self._flip = self._rotate = self.__error
raise NotImplementedError("{} should not be instantiated directly".format(type(self)))
def __error(self):
"""Raise error as certain methods need to be over-rided."""
raise NotImplementedError("method needs to be defined by sub-class")
Question
Is that an anti-pattern, like singletons or w/e? If so, what's a better way? Should I even check for this at all (i.e., maybe similar to excessive type-checking)?
Disclaimer: Python was my first language, and I do NOT like Java (although I have some experience from Android development); so this is not me trying to port some ruddy static pattern from another language.
Solution
With multi-inheritance and super, raising
Breaking the DRY principle
When
instead of something like this
Breaking inheritance
Do you really need a abstract base class
I feel that in Python, you do not need to prevent consumers of using your class a certain way. Maybe the base class could be used as a valid container. If so, returning a correct default (possibly
Here a similar version as your original of a plain matrix usable as a base class. But you should define each method to be able to add it's docstring.
You really need a a abstract class
Use abc.ABCMeta.
NotImplementedError is an anti-pattern.Breaking the DRY principle
When
__init__ raise a exception all subclass must repeat the initialization instead of using the default. class BadBaseMatrix():
"""Init raise NotImplementedError"""
def __init__(self, text_version_of_matrix):
"""text_version_of_matrix represent some argument to initialize all matrix"""
# ...
raise NotImplementedError
class ComplexMatrix(BadBaseMatrix):
def __init__(self, text_version_of_matrix):
self.text_version_of_matrix = text_version_of_matrix
# ...
class OtherMatrix(BadBaseMatrix):
def __init__(self, text_version_of_matrix):
# Must redo the initialization here
self.text_version_of_matrix = text_version_of_matrix
# ...instead of something like this
class BetterBaseMatrix():
def __init__(self, text_version_of_matrix):
"""text_version_of_matrix represent some argument to initialize all matrix"""
self.text_version_of_matrix = text_version_of_matrix
class ComplexMatrix(BetterBaseMatrix):
# ...
class PrintMatrix(BetterBaseMatrix):
def __init__(self, text_version_of_matrix):
super().__init__(text_version_of_matrix)
# in python 2, this would be super(MyMatrix, self)
print("PrintMatrix initialized")Breaking inheritance
class MyMatrix(BadBaseMatrix):
def__init__(self, text_version_of_matrix):
# I will use his implementation because it must do some important initialization there.
super().__init__(text_version_of_matrix)
>>> matrix = MyMatrix("")
# NotImplementedErrorDo you really need a abstract base class
I feel that in Python, you do not need to prevent consumers of using your class a certain way. Maybe the base class could be used as a valid container. If so, returning a correct default (possibly
None) would be enough for methods. Here a similar version as your original of a plain matrix usable as a base class. But you should define each method to be able to add it's docstring.
class PlainMatrix():
def _do_nothing(*args, **kwargs):
"""This docstring is used for all methods ..."""
pass #
rotate = flip = _do_nothingYou really need a a abstract class
Use abc.ABCMeta.
import abc
class BaseMatrix(metaclass=abc.ABCMeta):
# In python 2,
# __metaclass__ = abc.ABCMeta
def __init__(self, text_version_of_matrix):
"""text_version_of_matrix represent some argument to initialize all matrix"""
self.text_version_of_matrix = text_version_of_matrix
@abc.abstractmethod
def rotate(self, degree):
""" Abstract rotate that must be implemented in subclass"""
pass
class SubMatrix(BaseMatrix):
def rotate(self, degree):
""" True implementation of rotate"""
# ...
class StillAbstractMatrix(BaseMatrix):
""" StillAbstractMatrix has not implemented rotate """
pass
>>> sub_matrix = SubMatrix("1 2 3")
>>> bad = StillAbstractMatrix("1 2 3")
# Traceback (most recent call last):
# File "", line 1, in
# TypeError: Can't instantiate abstract class StillAbstractMatrix with abstract methods rotateCode Snippets
class BadBaseMatrix():
"""Init raise NotImplementedError"""
def __init__(self, text_version_of_matrix):
"""text_version_of_matrix represent some argument to initialize all matrix"""
# ...
raise NotImplementedError
class ComplexMatrix(BadBaseMatrix):
def __init__(self, text_version_of_matrix):
self.text_version_of_matrix = text_version_of_matrix
# ...
class OtherMatrix(BadBaseMatrix):
def __init__(self, text_version_of_matrix):
# Must redo the initialization here
self.text_version_of_matrix = text_version_of_matrix
# ...class BetterBaseMatrix():
def __init__(self, text_version_of_matrix):
"""text_version_of_matrix represent some argument to initialize all matrix"""
self.text_version_of_matrix = text_version_of_matrix
class ComplexMatrix(BetterBaseMatrix):
# ...
class PrintMatrix(BetterBaseMatrix):
def __init__(self, text_version_of_matrix):
super().__init__(text_version_of_matrix)
# in python 2, this would be super(MyMatrix, self)
print("PrintMatrix initialized")class MyMatrix(BadBaseMatrix):
def__init__(self, text_version_of_matrix):
# I will use his implementation because it must do some important initialization there.
super().__init__(text_version_of_matrix)
>>> matrix = MyMatrix("")
# NotImplementedErrorclass PlainMatrix():
def _do_nothing(*args, **kwargs):
"""This docstring is used for all methods ..."""
pass #
rotate = flip = _do_nothingimport abc
class BaseMatrix(metaclass=abc.ABCMeta):
# In python 2,
# __metaclass__ = abc.ABCMeta
def __init__(self, text_version_of_matrix):
"""text_version_of_matrix represent some argument to initialize all matrix"""
self.text_version_of_matrix = text_version_of_matrix
@abc.abstractmethod
def rotate(self, degree):
""" Abstract rotate that must be implemented in subclass"""
pass
class SubMatrix(BaseMatrix):
def rotate(self, degree):
""" True implementation of rotate"""
# ...
class StillAbstractMatrix(BaseMatrix):
""" StillAbstractMatrix has not implemented rotate """
pass
>>> sub_matrix = SubMatrix("1 2 3")
>>> bad = StillAbstractMatrix("1 2 3")
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: Can't instantiate abstract class StillAbstractMatrix with abstract methods rotateContext
StackExchange Code Review Q#47059, answer score: 9
Revisions (0)
No revisions yet.