【问题标题】:Differences in three ways to define a abstract class定义抽象类的三种方式的区别
【发布时间】:2018-11-07 19:04:16
【问题描述】:

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

三个例子的主要区别(见下面的代码)是:

  • A 明确设置一个新元类abc.ABCMeta
  • B 继承自 abc.ABC
  • C 继承自 objects 但定义了 @abc.abstractmethod

似乎AB 没有区别(即B 也有新的元类abc.ABCMeta)。但是,C 类仍然是 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("---")

【问题讨论】:

  • “尽管如此,C 类的行为似乎符合我对 ABC 的期望。” – 究竟是什么意思?您期望什么以及它的表现如何?

标签: python python-3.x abstract-class


【解决方案1】:

abc.ABCMeta 类对于实际强制执行 abstractmethod 行为是必需的。它的目的是禁止实例化任何不实现抽象方法的类。装饰器本身不能强制执行,元类在实例化时强制执行装饰器:

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

Foo()  # works

但是:

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

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

因此,没有元类,abstractmethod 装饰器不会做任何事情。

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

一个以ABCMeta 作为元类的辅助类。有了这堂课, 可以通过简单地从ABC 派生来创建抽象基类 避免有时混淆元类的使用 [..]

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-05
    • 2012-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    相关资源
    最近更新 更多