繁体   English   中英

使用抽象基 class VS 普通 inheritance

[英]Using abstract base class VS plain inheritance

我试图了解使用抽象基类的好处。 考虑以下两段代码:

抽象基础 class:

from abc import ABCMeta, abstractmethod, abstractproperty

class CanFly:
    __metaclass__ = ABCMeta

    @abstractmethod
    def fly(self):
        pass

    @abstractproperty
    def speed(self):
        pass


class Bird(CanFly):

    def __init__(self):
        self.name = 'flappy'

    @property
    def speed(self):
        return 1

    def fly(self):
        print('fly')


b = Bird()

print(isinstance(b, CanFly))  # True
print(issubclass(Bird, CanFly))  #  True

普通 inheritance:

class CanFly(object):

    def fly(self):
        raise NotImplementedError

    @property
    def speed(self):
        raise NotImplementedError()


class Bird(CanFly):

    @property
    def speed(self):
        return 1

    def fly(self):
        print('fly')


b = Bird()

print(isinstance(b, CanFly))  # True
print(issubclass(Bird, CanFly))  # True

如您所见,这两种方法都支持使用isinstanceissubclass的变形。

现在,我知道的一个区别是,如果您尝试实例化抽象基础 class 的子类而不覆盖所有抽象方法/属性,那么您的程序将失败。 但是,如果您将普通的 inheritance 与NotImplementedError一起使用,则在您实际调用相关方法/属性之前,您的代码不会失败。

除此之外,使用抽象基础 class 有何不同?

除了您在问题中提到的内容之外,就具体细节而言,最值得注意的答案是@abstractmethod@abstractproperty 1装饰器的存在,以及从ABC继承(或具有ABCMeta元类)阻止您实例化 object一点也不。

from abc import ABC, abstractmethod

class AbsParent(ABC):
    @abstractmethod
    def foo(self):
        pass

AbsParent()

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class AbsParent with abstract methods foo

然而,这里还有更多可玩的地方。 PEP 3119中的 Python 中引入了抽象基类。 我建议通读“基本原理”部分,了解 Guido 对为什么首先引入它们的看法。 我大二的总结是,他们不太关心他们的具体特征,而更多地关注他们的哲学。 他们的目的是向外部检查员发出信号,表明 object 是从 ABC 继承的,并且由于它是从 ABC 继承的,因此它将遵循善意的协议。 这个“善意协议”是孩子object会遵从父母的意愿 本协议的实际执行由您自己决定,这就是为什么它是一个善意的协议,而不是一个明确的合同。

这主要通过register()方法的镜头显示出来。 任何以ABCMeta作为元类(或简单地从ABC继承)的 class 都将有一个register()方法。 通过使用 ABC 注册 class ,您表示它从 ABC 继承,即使它在技术上没有。 这就是善意协议的用武之地。

from abc import ABC, abstractmethod

class MyABC(ABC):
    @abstractmethod
    def foo(self):
        """should return string 'foo'"""
        pass


class MyConcreteClass(object):
    def foo(self):
        return 'foo'

assert not isinstance(MyConcreteClass(), MyABC)
assert not issubclass(MyConcreteClass, MyABC)

虽然MyConcreteClass在这一点上与MyABC无关,但它确实根据评论中列出的要求实现了 MyABC 的MyABC 现在,如果我们用MyABC注册MyConcreteClass ,它将通过isinstanceissubclass检查。

MyABC.register(MyConcreteClass)

assert isinstance(MyConcreteClass(), MyABC)
assert issubclass(MyConcreteClass, MyABC)

再次,这就是“诚信协议”发挥作用的地方。 不必遵循 MyABC 中列出的MyABC 通过向 ABC 注册具体的 class,我们告诉任何外部检查员,我们,程序员,遵守我们应该遵守的 API。

1请注意, @abstractproperty不再是首选。 相反,您应该使用:

@property
@abstractmethod
def foo(self):
    pass

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM