问题
So, here is a problem:
I want to define an abstract class, let's say
AbstractA, which does not require subclasses to implement any of its methods, but rather to extend its functionality. In terms of Java that would be interface class.Moreover, I want to be able to create an abstract subclass, let's say
AbstractB, of theAbstractAwith the same properties, but some methods redefining or extending base class methods.
I don't want though to make class (AbstractA) abstract e.g. through the check of class name in __init__ or __new__, because that would require abstract subclass (AbstractB) to redefine that method along with it's main functionality, i.e. construction or initialization of a new instance. Or to call super().__init__(...) which I'd prefer to avoid as well (maybe I'm wrong here).
So, I want something like that:
class AbstractA:
def __init__(self):
# do initialization stuff
def very_common_method(self, ...):
# do very common stuff
class AbstractB(AbstractA):
# do not duplicate initialization stuff here, inherit instead
def less_common_method(self, ...):
# do less common stuff
class AX(AbstractA):
def specific_method_1(self, ...):
class BX(AbstractB):
def specific_method_2(self, ...):
# Instantiating AbstractA or AbstractB should result in error.
# Instantiating AX or BX should not.
Below I have a possible solution. Is there any disadvantages I overlook? Better solution?
Thanks!
回答1:
Here's possible solution:
class AbstractA:
_is_abstract = True
def __init__(self):
if self._is_abstract:
raise RuntimeError("Abstract class instantiation.")
# do initialization stuff
def __init_subclass__(self): # is called every time class is subclassed
self._is_abstract = False # thus makes sure abstract check fails on a subclass
class AbstractMixin:
def __init_subclass__(self):
self._is_abstract = True
class AbstractB(AbstractMixin, AbstractA): # AbstractMixin takes precendence on MRO,
# inherit __init__ # so the class abstract check returns True.
def __init_subclass__(self):
self._is_abstract = False
class A(AbstractA):
pass
class B(AbstractB):
pass
AbstractA()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __init__
RuntimeError: Abstract class instantiation.
AbstractB()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __init__
RuntimeError: Abstract class instantiation.
A()
<__main__.A object at 0x7f0bba5112e8>
B()
<__main__.B object at 0x7f0bba511438>
回答2:
class A(object):
def __init__(self):
if self.__class__ == A:
raise RuntimeError("Abstract class instantiation.")
print(self.__class__.__name__)
class B(A):
pass
>>> A()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __init__
RuntimeError: Abstract class instantiation.
>>> B()
B
<__main__.B object at 0x7f8c816a58d0>
>>>
回答3:
In Python, you would probably implement the "abstract" base classes as mix-ins. There's nothing special you need to do; by convention, you would add Mixin to the name to indicate that it isn't meant to be instantiated directly, but simply used as a base class for other classes.
class AMixin:
def __init__(self):
# do initialization stuff
def very_common_method(self, ...):
# do very common stuff
class BMixin(AMixin):
# do not duplicate initialization stuff here, inherit instead
def less_common_method(self, ...):
# do less common stuff
class AX(AMixin):
def specific_method_1(self, ...):
class BX(BMixin):
def specific_method_2(self, ...):
class Foo:
...
class Bar(Foo, BMixin):
...
来源:https://stackoverflow.com/questions/59316282/python-hierarchy-of-abstract-classes-without-abstract-methods