ABC抽象类

1、抽象类写法

如果要创建一个ABC的抽象类,则需要继承ABC,或者指定 metaclass=ABCMeta

使用继承ABC的写法:

from abc import ABC

class MyABC(ABC):
    pass

使用指定metaclass的写法:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

2、抽象类注册

把一个对象注册为抽象类,使用 class.register()

from abc import ABC

class MyABC(ABC):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)
MyABC为抽象类,使用MyABC.register()方式,将tuple注册给了抽象类。
所以用issubclass和isinstance验证为true。

3、自定义子类

使用 __subclasshook__ ,自定义行为来匹配是否属于子类。
__subclasshook__ 返回值有 True,``False``,``NotImplemented``。
True 则该类属于抽象类的subclass和instance。
False 则不属于subclass或instance。
NotImplemented 则根据其他方法继续检查是否符合。

一个__subclasshook__的例子:

from abc import ABC, abstractmethod
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

print(isinstance(Foo(), MyIterable))
MyIterable.register(Foo)
print(isinstance(Foo(), MyIterable))

上述代码,MyIterable为抽象类,里面定义了__subclasshook__方法,判断的依据是,被企望查询的类(代码中被企望查询的类为Foo),其父类以及所有继承类中的属性和方法时候包含__iter__,如果包含,则返回True,也就是属于,反之返回NotImplemented,进行其他方式查询,比如看Foo是否是继承了MyIterable类。

因为Foo继承object基类,而且其方法和属性都不存在__iter__,同时,Foo也没有继承自MyIterable,所以第一条打印的内容为False。

随后,通过MyIterable.register()的方法注册了Foo这个类。所以Foo也被认定为MyIterable的子类,所以最后一条打印为True。

修改一下代码,给Foo增加__iter__方法:

from abc import ABC, abstractmethod
class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def __iter__(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
print(isinstance(Foo(), MyIterable))

此时,打印出来的结果为True。

4、抽象类实例化

抽象类无法直接被实例化,需要通过继承的方法进行实例化。
若抽象类存在 abstractmethod 装饰器的方法,在继承的子类中必须存在,否则实例化子类会失败。

抽象类实例化例子:

from abc import ABCMeta, abstractmethod
class A(metaclass=ABCMeta):
    def my_normal_method(self):
        pass

    @abstractmethod
    def my_abstract_method(self):
        pass

class B(A):
    pass

class C(A):
    def my_abstract_method(self):
        pass

B()
C()

执行失败输出内容:

TypeError: Can't instantiate abstract class B with abstract methods my_abstract_method
此时如果执行,因为B类继承了抽象A类,但B中没有实现A中存在装饰器abstractmethod的my_abstract_method方法,所以报错了。
C类因为定义了 my_abstract_method ,所以实例化的时候不会报错。

5、抽象类结合常见装饰器

只需要在装饰器下面添加 @abstractmethod 即可

@classmethod

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

@staticmethod

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...

@property

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

@类名.setter

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

6、 抽象类cache_token()方法

token定义了当前针对虚拟接口的抽象基类的cache版本。 每次进行register()操作都会刷新token。

一个简单的例子:

import abc
class A(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def walk(self):
        pass
    def get_token(self):
        return abc.get_cache_token()

class B(A):
    def walk(self):
        pass

class C(object):
    pass

print(B().get_token())

A.register(C)

print(B().get_token())

输出结果为:

36
37

输出第一次为36,第二次因为调用了register方法,所以变成了37