两个 mixin 类将需求指定为抽象方法。这些类共同拥有一整套具体方法。然而,它们无法组合成一个具体的类:无论我使用哪种顺序来声明具体的类,一些抽象方法都会覆盖具体的方法。
有没有办法防止抽象方法覆盖具体方法?我相信这在 Scala 中是有效的。
指定要求的替代方法有哪些?
这是一个具体的例子:
import abc
class A(abc.ABC):
@abc.abstractmethod
def x(self):
pass
def y(self):
return "A.y"
class B(abc.ABC):
@abc.abstractmethod
def y(self):
pass
def x(self):
return f"B.x"
class AB(B, A):
pass
class BA(A, B):
pass
ab = AB() # TypeError: Can't instantiate abstract class AB with abstract methods y
ba = BA() # TypeError: Can't instantiate abstract class BA with abstract methods x
这里的问题是,当为一个或多个抽象类的子类计算抽象方法时,只会查找子类本身,而不是所有基类,以确定抽象方法是否已被具体定义。
因为我们从 PEP-3119 知道抽象类的抽象方法的名称存储在
__abstractmethods__
属性中,并且只有抽象方法的 __isabstractmethod__
设置为 True
:
装饰器设置函数属性@abstractmethod
为值 True。__isabstractmethod__
方法将类型属性ABCMeta.__new__
计算为所有属性的集合 具有__abstractmethods__
属性的方法名称,其值 是真的。__isabstractmethod__
我们可以在实例化类后用额外的逻辑覆盖
ABCMeta.__new__
,以检查原始计算中被认为保持抽象的方法是否实际上可以在任何基类中具体定义,在这种情况下定义对它们在子类中,并从子类的__abstractmethods__
中删除它们:
class ConprehensiveABCMeta(abc.ABCMeta):
_NOTFOUND = object()
def __new__(metacls, name, bases, namespace, /, **kwargs):
cls = super().__new__(metacls, name, bases, namespace, **kwargs)
abstracts = set(cls.__abstractmethods__)
for name in cls.__abstractmethods__:
for base in bases:
value = getattr(base, name, cls._NOTFOUND)
if not (value is cls._NOTFOUND or
getattr(value, '__isabstractmethod__', False)):
setattr(cls, name, value)
abstracts.remove(name)
break
cls.__abstractmethods__ = frozenset(abstracts)
return cls
class ConprehensiveABC(metaclass=ConprehensiveABCMeta):
pass
这样:
class A(ConprehensiveABC):
@abc.abstractmethod
def x(self):
pass
def y(self):
return "A.y"
class B(ConprehensiveABC):
@abc.abstractmethod
def y(self):
pass
def x(self):
return "B.x"
class AB(B, A):
pass
ab = AB()
print(ab.x())
print(ab.y())
输出:
B.x
A.y