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__方法。
posted @ 2022-03-14 16:29  肖肖凯  阅读(379)  评论(0)    收藏  举报