[python] 使用抽象基类(abc — Abstract Base Classes)验证接口(interface verification)

Why use Abstract Base Classes?

当你不太熟悉某个应用程序的源码,又想开发基于这个应用程序的plug-in extentions,你就需要了解程序中定义的抽象基类。同样的,在大型团队或大型代码库上工作时了解抽象类也很有必要,因为你很难同时track所有的类...

How ABCs Work

抽象基类和普通类的区别就是:

  1. 抽象基类本身会被标记为metaclass(基元类)
  2. 在抽象基类中一些函数会被标记为abstract method,所有继承抽象基类的子类中都需要override这些method,否则会报错(通过报错信息就可以很方便地排查你写的新类有哪些地方没实现)

将抽象基类标记成metaclass有两种方法: 注册直接继承metaclass

把抽象基类的函数标记成抽象函数使用装饰器 @abc.abstractmethod

Registering a Concrete Class

下面的@PluginBase.register这个装饰器的作用就是把 RegisteredImplementation注册为一个抽象基类

import abc
from abc_base import PluginBase


class LocalBaseClass:
    pass


@PluginBase.register
class RegisteredImplementation(LocalBaseClass):

    def load(self, input):
        return input.read()

    def save(self, output, data):
        return output.write(data)


if __name__ == '__main__':
    print('Subclass:', issubclass(RegisteredImplementation,
                                  PluginBase))
    print('Instance:', isinstance(RegisteredImplementation(),
                                  PluginBase))

'''
$ python3 abc_register.py

Subclass: True
Instance: True
'''

Implementation Through Subclassing 

可以看多下面的PluginBase类是直接继承了ABCMeta类,这使得他也自动获得抽象基类特性.

函数load和save前面的@abc.abstractmethod装饰器则是将load和save标记为需要子类来实现的抽象函数

import abc


class PluginBase(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def load(self, input):
        """
        Retrieve data from the input source and return an object.
        you need to implement it in subclass.
        """

    @abc.abstractmethod
    def save(self, output, data):
        """
        Save the data object to the output.
        you need to implement it in subclass.
        """

class SubclassImplementation(PluginBase):

    def load(self, input):
        return input.read()

    def save(self, output, data):
        return output.write(data)


if __name__ == '__main__':
    print('Subclass:', issubclass(SubclassImplementation,
                                  PluginBase))
    print('Instance:', isinstance(SubclassImplementation(),
                                  PluginBase))

'''
$ python3 abc_subclass.py

Subclass: True
Instance: True
'''

使用继承的方法还有一个好处:可以从抽象基类中获取所有实现了抽象函数的子类

import abc
from abc_base import PluginBase
import abc_subclass
import abc_register

for sc in PluginBase.__subclasses__():
    print(sc.__name__)

'''
$ python3 abc_find_subclasses.py

SubclassImplementation
'''

 Incomplete Implementations

如果你在子类中没有实现抽象函数,会报错

import abc
from abc_base import PluginBase


@PluginBase.register
class IncompleteImplementation(PluginBase):

    def save(self, output, data):
        return output.write(data)


if __name__ == '__main__':
    print('Subclass:', issubclass(IncompleteImplementation,
                                  PluginBase))
    print('Instance:', isinstance(IncompleteImplementation(),
                                  PluginBase))

'''
$ python3 abc_incomplete.py

Subclass: True
Traceback (most recent call last):
  File "abc_incomplete.py", line 24, in <module>
    print('Instance:', isinstance(IncompleteImplementation(),
TypeError: Can't instantiate abstract class
IncompleteImplementation with abstract methods load
'''

 Concrete Methods in ABCs

如果你在基类中也实现了抽象函数,那么在实例化子类的时候, 基类的抽象函数也会被调用(所以最好抽象基类中不要implement那些抽象函数)

import abc
import io


class ABCWithConcreteImplementation(abc.ABC):

    @abc.abstractmethod
    def retrieve_values(self, input):
        print('base class reading data')
        return input.read()


class ConcreteOverride(ABCWithConcreteImplementation):

    def retrieve_values(self, input):
        base_data = super(ConcreteOverride,
                          self).retrieve_values(input)
        print('subclass sorting data')
        response = sorted(base_data.splitlines())
        return response


input = io.StringIO("""line one
line two
line three
""")

reader = ConcreteOverride()
print(reader.retrieve_values(input))
print()

'''
$ python3 abc_concrete_method.py

base class reading data
subclass sorting data
['line one', 'line three', 'line two']
'''

除了抽象基类外,想实现接口检查功能还可以通过NotImplementedError

class Base(object):
    def go(self):
        raise NotImplementedError("Please Implement this method")


class Specialized(Base):
    def go(self):
        print "Consider me implemented"

 参考链接

  1. https://stackoverflow.com/questions/4382945/abstract-methods-in-python
  2. https://pymotw.com/3/abc/
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值