python接口与协议
接口与协议定义
接口
- 类实现或继承的公开属性,包括特殊方法,都是类的接口。即可以为外界所知的都是接口。受保护的属性和私有属性不是接口,虽然在python中并没有真正的受保护属性和私有属性。接口有两类,一类是正式的,即抽象基类。一类是非正式的,即协议。
协议
- 协议也是接口,但不是正式的,协议只由文档了约定定义。比如python最基础的协议之一,序列协议。即使对象只实现了该协议中最基础的部分,解释器也会负责人地处理。
鸭子类型与、白鹅类型、抽象基类
-
鸭子类型。忽略对象真正类型,转而关注对象有没有实现所需的方法、签名和语义。对python来说,意味着避免使用instance(type)检查对象类型。
-
白鹅类型。若cls是抽象基类,则可以使用isinstance(obj, cls)来判断对象obj是否是它的子类。但是obj不用继承自cls,可以使用register类方法把obj声明为抽象基类cls的虚拟子类,obj称为被注册的类,需要满足抽象基类对方法名称和签名的要求,满足底层语义契约。开发obj时不用了解抽象基类,更不用继承抽象基类。下边例子将TomboList注册为Tombola的虚拟子类。
![]()
-
有时让抽象基类识别子类甚至不用注册,只需要实现了特定的方法即可。
![]()
-
python中的抽象基类只要定义在numbers、collections.abc这两个模块中。
-
继承一个抽象基类必须实现其抽象方法,但是不用实现其具体方法。如有没有实现所有的抽象方法,则得到的还是抽象基类。
-
定义抽象方法用装饰器 @abc.abstractmethod
-
不要随便自己定义抽象基类或元类!
标准库中的抽象基类
collections.abc模块与abc模块
-
标准库中有两个名为abc的模块,collections.abc和abc
-
collections.abc中定义了16个抽象基类
![]()
-
abc模块中定义了abc.ABC类,每个抽象基类都依赖于这个类,只有在定义新的抽象基类时才需要导入abc模块。
numbers模块
- numbers模块定义的是数字塔,即各个抽象基类的层次结构是线性的,其中Number是位于最顶端的超类,随后是Complex类,最底端是Integral类。
![]()
- 若要检查一个数是不是整数,使用instance(x, numbers.Integral),这样可以兼容int、bool、外部库注册的其它类型。
- 若要检查一个数是不是浮点数类型,可以用instance(x, numbers.Real)。可以兼容bool、int、float、fractions.Fraction以及外部库提供了非复数类型,比如numpy中的类型(其做了注册)。
猴子补丁
- 在运行时修改类或模块,而不改动源码的技术,叫猴子补丁。如下例子中使用猴子补丁技术实现了__setitem__方法。
![]()





浙公网安备 33010602011771号