Der*_*ang 7 python abstract-base-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
Run Code Online (Sandbox Code Playgroud)
简单继承:
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
Run Code Online (Sandbox Code Playgroud)
如您所见,这两种方法都支持使用isinstance和进行变形issubclass.
现在,我知道的一个区别是,如果您尝试实例化抽象基类的子类而不覆盖所有抽象方法/属性,那么您的程序将大声失败.但是,如果使用普通继承NotImplementedError,则在实际调用相关方法/属性之前,代码不会失败.
除此之外,是什么让使用抽象基类不同?
Sho*_*nin 12
@abstractmethod除了您在问题中提到的内容之外,就具体细节而言最值得注意的答案是或@abstractproperty1装饰器的存在以及继承ABC(或具有ABCMeta元类)根本阻止您实例化该对象。
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
Run Code Online (Sandbox Code Playgroud)
然而,这里还有更多的事情在起作用。Python 在PEP 3119中引入了抽象基类。我建议您阅读“基本原理”部分,了解 Guido 对最初引入它们的看法。我的二年级总结是,它们较少关注其具体特征,而更多关注其哲学。它们的目的是向外部检查员发出信号,表明该对象继承自 ABC,并且由于它继承自 ABC,因此它将遵循善意协议。这种“善意协议”就是子对象将遵循父对象的意图。本协议的实际执行由您决定,这就是为什么它是善意协议,而不是明确的合同。
这主要通过方法的镜头表现出来register()。任何具有ABCMeta其元类(或简单地继承自ABC)的类都将具有一个register()方法。通过使用 ABC 注册一个类,您就表明它继承自 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)
Run Code Online (Sandbox Code Playgroud)
虽然MyConcreteClass,在这一点上与 无关,但它确实根据注释中列出的要求MyABC实现了 API 。MyABC现在,如果我们注册MyConcreteClass,MyABC它将通过isinstance并issubclass检查。
MyABC.register(MyConcreteClass)
assert isinstance(MyConcreteClass(), MyABC)
assert issubclass(MyConcreteClass, MyABC)
Run Code Online (Sandbox Code Playgroud)
这又是“善意协议”发挥作用的地方。您不必遵循 中列出的 APIMyABC。通过向 ABC 注册具体类,我们告诉任何外部检查员,我们程序员正在遵守我们应该遵守的 API。
1请注意,这@abstractproperty不再是首选。相反,你应该使用:
@property
@abstractmethod
def foo(self):
pass
Run Code Online (Sandbox Code Playgroud)