Python abc—抽象基类

Yujiaao

该模块提供了在Python中定义抽象基类(ABC - Abstract Base Class)的基础结构,参考PEP 3119;至于为何将其添加到 Python,也可以看看PEP 3141numbers模块有关基于ABC的 numbers 的类层次结构的模块。

容器collections模块具有一些衍生自ABC的具体类。当然,这些可以进一步继承衍生。此外, collections.abc子模块具有一些 ABC,可用于测试:类或实例是否提供特定的接口,例如,是否可哈希或是否为映射。

此模块提供ABCMeta用于定义ABC 的元类和帮助程序类,ABC以通过继承来替代地定义ABC:

classabc.`ABC

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

from abc import ABC

class MyABC(ABC):
    pass

请注意,类型ABC为still ABCMeta,因此从继承继承ABC需要有关元类使用的常规预防措施,因为多重继承可能会导致元类冲突。也可以通过传递 metaclass 关键字并ABCMeta直接使用来定义抽象基类,例如:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

3.4版的新功能。

class abc.`ABCMeta`
用于定义抽象基类(ABC)的元类。

使用此元类创建一个ABC。ABC可以直接子类化,然后充当混合类。您还可以将不相关的具体类(甚至是内置类)和不相关的ABC注册为“虚拟子类” –内置issubclass()函数会将它们及其后代视为注册ABC的子类,但是注册ABC不会显示在其 MRO(方法解决顺序)中,由注册ABC定义的方法实现也将不可调用(甚至不能通过调用 super())。1个

使用元类创建的类ABCMeta具有以下方法:

register(_subclass_)

将__subclass__注册为该ABC的“虚拟子类”。例如:

from abc import ABC

class MyABC(ABC):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

在版本3.3中更改:返回注册的子类,以允许用作类装饰器。

在版本3.4中更改:要检测对的调用register(),可以使用该 get_cache_token()功能。

您还可以在抽象基类中重写此方法:

__subclasshook__(_子类_)

(必须定义为类方法。)

检查_子类_是否被视为此ABC的子类。这意味着您可以自定义issubclass进一步的行为,而无需调用register()要考虑为ABC的子类的每个类。(此类方法是从__subclasscheck__()ABC 的方法中调用的。)

这个方法应该返回TrueFalseNotImplemented。如果返回True,则将该_子类_视为此ABC的子类。如果返回False,则即使该子类通常是一个_子类,_也不会将该_子类_视为该ABC的子类。如果返回 NotImplemented,则使用常规机制继续子类检查。

为了演示这些概念,请看以下示例ABC定义:

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

MyIterable.register(Foo)

ABC MyIterable将标准可迭代方法定义 __iter__()为抽象方法。此处给出的实现仍可以从子类中调用。该get_iterator()方法也是MyIterable抽象基类的一部分,但是在非抽象派生类中不必重写此方法~~~~。

__subclasshook__()此处定义的类方法表示,任何__iter__()在其类__dict__(或通过__mro__列表访问的基类之一)中具有方法的 类也被视为类MyIterable

最后,即使没有定义方法,最后一行仍是Foo的虚拟子类(它使用按照和 定义的旧式可迭代协议)。请注意,这不能 作为的方法使用,因此是单独提供的。MyIterable__iter__()__len__()__getitem__()get_iterator`Foo`

abc模块还提供以下装饰器:

@`abc.`abstractmethod[](https://docs.python.org/3/lib... "此定义的永久链接")

装饰器,指示抽象方法。

使用此装饰器要求该类的元类是ABCMeta 或从其派生的。ABCMeta除非实例化了其所有抽象方法和属性,否则无法实例化具有派生自其的元类的类 。可以使用任何正常的“超级”调用机制来调用抽象方法。 abstractmethod()可以用来声明属性和描述符的抽象方法。

不支持将动态方法添加到类,或在创建方法或类后尝试修改其抽象状态。将abstractmethod()仅影响使用常规继承派生的子类; 使用ABC register()方法注册的“虚拟子类” 不受影响。

abstractmethod()与其他方法描述符结合使用时,应将其用作最里面的装饰器,如以下用法示例所示:~~~~

class C(ABC):
    @abstractmethod
    def my_abstract_method(self, ...):
        ...
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, ...):
        ...
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...

    @property
    @abstractmethod
    def my_abstract_property(self):
        ...
    @my_abstract_property.setter
    @abstractmethod
    def my_abstract_property(self, val):
        ...

    @abstractmethod
    def _get_x(self):
        ...
    @abstractmethod
    def _set_x(self, val):
        ...
    x = property(_get_x, _set_x)

为了正确地与抽象基类机制互操作,描述符必须使用标识自己为抽象 __isabstractmethod__。通常,True 如果用于构成描述符的任何方法都是抽象的,则此属性应为。例如,Python的内置功能property等效于:

class Descriptor:
    ...
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for f in (self._fget, self._fset, self._fdel))
注意

与Java抽象方法不同,这些抽象方法可能具有实现(Java新版也有接口默认实现)。可以通过super()覆盖它的类中的机制来调用此实现。在使用协作式多重继承的框架中,这可用作超级调用的端点。

abc模块还支持以下旧式装饰器:

@`abc.`abstractclassmethod[](https://docs.python.org/3/lib... "此定义的永久链接")

3.2版中的新功能。

自从3.3版本不推荐使用:现在可以使用classmethodabstractmethod(),使这个装饰是多余的。

内置的子类classmethod(),指示抽象的类方法。否则它类似于abstractmethod()

不建议使用这种特殊情况,因为classmethod()现在将装饰器应用于抽象方法时,可以正确地将其标识为抽象:

class C(ABC):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, ...):
        ...

@`abc.`abstractstaticmethod
3.2版中的新功能。

自从3.3版本不推荐使用:现在可以使用staticmethodabstractmethod(),使这个装饰是多余的。

内置的子类staticmethod(),指示抽象的静态方法。否则它类似于abstractmethod()

不建议使用这种特殊情况,因为staticmethod()现在将装饰器应用于抽象方法时,可以正确地将其标识为抽象:

class C(ABC):
    @staticmethod
    @abstractmethod
    def my\_abstract\_staticmethod(...):
        ...

@abc.abstractproperty

自从3.3版本不推荐使用:现在可以使用propertyproperty.getter()property.setter()property.deleter()abstractmethod(),使这个装饰是多余的。

内置的子类property(),指示抽象属性。

不建议使用这种特殊情况,因为property()现在将装饰器应用于抽象方法时,可以正确地将其标识为抽象:

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

上面的示例定义了一个只读属性;您还可以通过适当地将一个或多个基础方法标记为抽象来定义读写抽象属性:

class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

如果只有某些组件是抽象的,则仅需要更新那些组件即可在子类中创建具体属性:

class D(C):
    @C.x.setter
    def x(self, val):
        ...

abc模块还提供以下功能:

abc.get_cache_token()

返回当前抽象基类缓存令牌。

令牌是一个不透明的对象(支持相等性测试),用于标识虚拟子类的抽象基类缓存的当前版本。令牌随着ABCMeta.register()在任何ABC上的每次调用而改变。

3.4版的新功能。

脚注

1个

C++程序员应注意,Python的虚拟基类概念与C++不同。

阅读 506
11.9k 声望
3.7k 粉丝
0 条评论
11.9k 声望
3.7k 粉丝
宣传栏