Python mixin 与抽象方法覆盖具体方法

问题描述 投票:0回答:1

两个 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
python multiple-inheritance mixins method-resolution-order
1个回答
0
投票

这里的问题是,当为一个或多个抽象类的子类计算抽象方法时,只会查找子类本身,而不是所有基类,以确定抽象方法是否已被具体定义。

因为我们从 PEP-3119 知道抽象类的抽象方法的名称存储在

__abstractmethods__
属性中,并且只有抽象方法的
__isabstractmethod__
设置为
True
:

@abstractmethod
装饰器设置函数属性
__isabstractmethod__
为值 True。
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

演示:https://ideone.com/jFMuII

© www.soinside.com 2019 - 2024. All rights reserved.