定义抽象类的三种方式的差异

no_*_*123 3 python abstract-class python-3.x

我发现了多种(略有不同)在Python中定义抽象类的方法。我阅读了文档,但在stackoverflow上也找不到答案。

这三个示例之间的主要区别(请参见下面的代码)是:

  • Aabc.ABCMeta显式设置新的元类
  • B 继承自 abc.ABC
  • C继承objects但定义@abc.abstractmethod

似乎AB没什么不同(即也B有新的metaclass abc.ABCMeta)。但是,类C仍然是type type

不为其定义元类有C什么影响?什么时候需要定义元类,或者不abc.ABCMeta为抽象类定义元类是错误/不好的风格?但是,该班级的C表现似乎符合我对ABC的期望。

import abc

class A(metaclass=abc.ABCMeta):
    # Alternatively put __metaclass__ = abc.ABCMeta here
    @abc.abstractmethod
    def foo(self):
        raise NotImplementedError


class B(abc.ABC):

    @abc.abstractmethod
    def foo(self):
        raise NotImplementedError


class C(object):

    @abc.abstractmethod
    def foo(self):
        raise NotImplementedError


class Aimpl(A):

    def foo(self):
        print("Aimpl")


class Bimpl(B):
    def foo(self):
        print("Bimpl")


class Cimpl(C):
    #def foo(self):
     #   print("Cimpl")
     pass

Aimpl().foo()                           # Aimpl
print(isinstance(Aimpl, A))             # False
print(issubclass(Aimpl, A))             # True
print(isinstance(Aimpl, abc.ABCMeta))   # True
print(type(A))                          # <class 'abc.ABCMeta'>
print("---")

Bimpl().foo()                           # Bimpl
print(isinstance(Bimpl, B))             # False
print(issubclass(Bimpl, B))             # True
print(isinstance(Bimpl, abc.ABCMeta))   # True
print(type(B))                          # <class 'abc.ABCMeta'>
print("---")

Cimpl().foo()                           # Cimpl
print(isinstance(Cimpl, C))             # False
print(issubclass(Cimpl, C))             # True
print(isinstance(Cimpl, abc.ABCMeta))   # False
print(type(C))                          # <class 'type'>
print("---")
Run Code Online (Sandbox Code Playgroud)

dec*_*eze 5

abc.ABCMeta班是需要实际执行abstractmethod的行为。其目的是禁止实例化未实现abstract方法的任何类。装饰器本身不能强制执行该操作,元类在实例化时强制装饰器:

class Foo:
    @abstractmethod
    def bar(self):
        pass

Foo()  # works
Run Code Online (Sandbox Code Playgroud)

然而:

class Foo(metaclass=ABCMeta):
    @abstractmethod
    def bar(self):
        pass

Foo()
Run Code Online (Sandbox Code Playgroud)
class Foo:
    @abstractmethod
    def bar(self):
        pass

Foo()  # works
Run Code Online (Sandbox Code Playgroud)

因此,没有元类,abstractmethod装饰器就什么也不做。

abc.ABC只是一个简写,所以您可以Foo(ABC)代替,仅此而已Foo(metaclass=ABCMeta)

具有ABCMeta作为其元类的帮助程序类。使用此类,可以通过ABC 避免有时混淆元类用法的简单派生来创建抽象基类[..]

https://docs.python.org/3/library/abc.html#abc.ABC