Fluent Python 3函数 7接口:从协议到抽象基类

目录

1.接口和协议

2.序列协议

3.猴子补丁

4.抽象基类

5.标准库中的抽象基类


1.接口和协议

duck-typing -- 鸭子类型

指一种编程风格,它并不依靠查找对象类型来确定其是否具有正确的接口,而是直接调用或使用其方法或属性(“看起来像鸭子,叫起来也像鸭子,那么肯定就是鸭子。”)由于强调接口而非特定类型,设计良好的代码可通过允许多态替代来提升灵活性。鸭子类型避免使用 type() 或 isinstance() 检测。(但要注意鸭子类型可以使用 抽象基类 作为补充。) 而往往会采用 hasattr() 检测或是 EAFP 编程。

鸭子类型:对象的类型无关紧要,只要实现了特定的协议即可。即忽略对象的真正类型,只需要关注对象是否实现了所需的方法、签名和语义。

接口在动态类型语言中是怎么运作的呢?

首先,Python 语言没有 interface 关键字,而且除了抽象基类,每个类都有接口:类实现或继承的公开属性(方法或数据属性),包括特殊方法,如__getitem__或__add__。

按照定义,受保护的属性和私有属性不在接口中。

不要觉得把公开数据属性放入对象的接口中不妥,因为如果需要,总能实现读值方法和设值方法,把数据属性变成特性,使用obj.attr句法的客户代码不会受到影响。

协议是接口,但不是正式的(只由文档和约定定义),因此协议不能像正式接口那样施加限制(本章后面会说明抽象基类对接口一致性的强制)。一个类可能只实现部分接口,这是允许的。

2.序列协议

序列协议是Python最基础的协议之一。即便对象只实现了那个协议最基本的一部分,解释器也会负责任地处理。

比如类中只实现了序列协议的一个方法: __getitem__ 方法,没有实现 __iter__ 和 __contains__ 方法,迭代和in运算符也可用。原因是 Python 会调用  __getitem__  方法,设法让迭代和in运算符可用。

3.猴子补丁

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

4.抽象基类

abstract base class -- 抽象基类

抽象基类简称 ABC,是对 duck-typing 的补充,它提供了一种定义接口的新方式,相比之下其他技巧例如 hasattr() 显得过于笨拙或有微妙错误(例如使用 魔术方法)。ABC 引入了虚拟子类,这种类并非继承自其他类,但却仍能被 isinstance() 和 issubclass() 所认可;详见 abc 模块文档。Python 自带许多内置的 ABC 用于实现数据结构(在 collections.abc 模块中)、数字(在 numbers 模块中)、流(在 io 模块中)、导入查找器和加载器(在 importlib.abc 模块中)。你可以使用 abc 模块来创建自己的 ABC

抽象基类的本质就是几个特殊方法。

白鹅类型

>>> class Struggle:
	def __len__(self):
		return 100

	
>>> from collections import abc
>>> isinstance(Struggle(),abc.Sized)
True
可以看出,无需注册, abc.Sized 也能把 Struggle 识别为自己的子类,只要实现了特殊方法 __len__ 即可。
 
继承抽象基类很简单,只需要实现所需的方法。
 
抽象基类是用于封装框架引入的一般性概念和抽象的,例如“一个序列”和“一个确切的数”。Python程序员基本上不需要自己编写新的抽象基类,只要正确使用现有的抽象基类,就能获得 99.9% 的好处,而不用冒着设计不当导致的巨大风险。
 
导入时(加载并编译frenchdeck2.py模块时),Python不会检查抽象方法的实现,在运行时实例化FrenchDeck2类时才会真正检查。因此,如果没有正确实现某个抽象方法,Python会抛出TypeError异常,并把错误消息设为"Can'tinstantiateabstractclassFrenchDeck2withabstractmethods__delitem__,insert"。正是这个原因,即便FrenchDeck2类不需要__delitem__和insert提供的行为,也要实现,因为MutableSequence抽象基类需要它们。
 

5.标准库中的抽象基类

 
大多数抽象基类在 collections.abc 模块中定义,不过其他地方也有。例如, numbers 和 io 包中有一些抽象基类。但是, collections.abc 中的抽象基类最常用。
 
collections.abc --- 容器的抽象基类
 
各个集合应该继承这三个抽象基类Iterable Container 和 Sized,以支持迭代、in运算符、Sized 通过 __len__() 方法支持 len() 函数。
 
不可变集合类型:Sequence Mapping Set
 
映射:MappingView ,映射方法 .items()、.keys() 和 .values() 返回 的对象分别是 ItemsView、KeysView 和 ValuesView 的实例。
 
Callable 和 Hashable,这两个抽象基类的主要作用是为内置函数 isinstance 提供支持,以一种安全的方式判断对象能不能调用或散列。
 
Iterator 是 Iterable 的子类。将在第 14 章详细讨论。
 
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值