Python基础之接口——从协议到抽象基类

导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。

本文重点:

1、协议是Python中非正式的接口;
2、了解抽象基类的基本概念以及标准库中的抽象基类;
3、掌握抽象基类的使用方法。

一、协议的高度动态本性

1、协议与接口的基本概念

协议:是Python中非正式的接口,是令Python这种动态类型语言实现多态的方式。
接口:泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。
类的接口:类实现或继承的公开属性(方法或数据属性),包括特殊方法,如__getitem__或__add__。

2、协议是非正式的

协议是非正式的,只由文档和约定定义,不具备强制性。
以序列协议为例,假设我们想实现迭代以及in运算,通常需要__iter__和__contains__方法,但事实上只实现__getitem__方法也可以。原因在于当Python发现没有__iter__和__contains__可用时,会转而调用__getitem__方法设法让迭代和in运算符可用。
小结:部分实现协议是有用的。

3、使用猴子补丁在运行时实现协议

在运行时修改类或模块而不改动源码,从而实现目标协议接口的操作就是打猴子补丁。

二、抽象基类

1、基本概念

鸭子类型(duck typing):不关注对象的类型,而是关注对象具有的行为(方法)。鸭子类型像多态一样工作,但是没有继承。
在鸭子类型中,协议风格的接口与继承完全没有关系,实现同一个协议的各个类是相互独立的。

白鹅类型(goose typing):只要cls是抽象基类,即cls的元类是abc.ABCMeta,就可以使用isinstance(obj,cls)。

抽象基类(abstract base class,ABC):抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现。抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。简言之,ABC描述的是至少使用一个纯虚函数的接口,从ABC派生出的类将根据派生类的具体特征,使用常规虚函数来实现这种接口。

2、标准库中的抽象基类

(1)collections.abc中抽象基类
collections.abc模块中各个抽象基类的UML类图如下所示:
Python基础之接口——从协议到抽象基类_第1张图片

  • Iterable、 Container 和 Sized
    各个集合应该继承这三个抽象基类, 或者至少实现兼容的协议。
    Iterable 通过 __iter__ 方法支持迭代;
    Container 通过__contains__ 方法支持 in 运算符;
    Sized 通过 __len__ 方法支持len() 函数。
  • Sequence、 Mapping 和 Set
    这三个是主要的不可变集合类型, 而且各自都有可变的子类。
  • MappingView
    在 Python 3 中, 映射方法 .items()、 .keys() 和 .values() 返回的对象分别是 ItemsView、 KeysView 和 ValuesView 的实例。 前两个类还从 Set 类继承了丰富的接口,涉及集合的全部运算符。
  • Callable 和 Hashable
    这两个抽象基类与集合没有太大的关系,只不过因为collections.abc 是标准库中定义抽象基类的第一个模块, 而它们又太重要了,因此才把它们放到 collections.abc 模块中。Callable 或 Hashable 的子类非常少见。这两个抽象基类的主要作用是为内置函数 isinstance 提供支持,以一种安全的方式判断对象能不能调用或散列。
  • Iterator
    是 Iterable 的子类。

(2)numbers包中的数字塔
按照自上而下的线性结构,Number是位于最顶端的超类,详细排序如下:

  • Number
  • Complex
  • Real
  • Rational
  • Integral

例如,检查一个数是否是整数,可以使用isinstance(x,numbers.Integral)。

三、抽象基类的使用

1、通过继承声明抽象基类

声明抽象基类最简单的方式是继承abc.ABC或其他抽象基类:Class Student(abc.ABC)
注意:在Python3.0~3.3之间,继承抽象基类的语法是:Class Student(metaclass=abc.ABCMeta)。

2、判断子类是否符合接口定义

在定义抽象基类的子类时,子类需要将继承的抽象基类中的抽象方法具体实现。

3、声明虚拟子类实现抽象基类的接口

虚拟子类:指的是不通过继承而利用注册把一个类变成抽象基类的子类。
注册虚拟之类的方式是调用register方法,语法是@抽象基类名称.register。
经注册后的虚拟子类可以被issubclass和isinstance等函数识别,但是注册的类不会从抽象基类中继承任何方法或属性。具体可通过类属性__mro__查看类的真实继承关系。

四、其它

1、抽象基类中声明抽象类方法需要使用@abc.abstractmethod标记,并且在@abc.abstractmethod和def之间不能有其他装饰器。
2、内省类的继承关系的方法:
__subclasses__():返回类的直接子类列表,不含虚拟子类。
__abcregistry:抽象基类独有的属性,是抽象类注册的虚拟子类的弱引用。
3、__subclasshook__:令抽象基类识别没有进行子类化和注册的类,此方法在abc.Sized中有应用。

你可能感兴趣的:(Python基础之接口——从协议到抽象基类)