Fluent_Python_Part4面向对象,11-iface-abc,协议(接口),抽象基类
第四部分第11章,接口:从协议到抽象基类(重点讲抽象基类)
- 接口就是实现特定角色的方法集合。
- 严格来说,协议是非正式的接口(只由文档约束),正式接口会施加限制(抽象基类对接口一致性的强制)。
- 在Python中,“X类对象”、“X协议”、“X接口”都是一个意思。如“文件类对象”、“可迭代对象”,指的不是特定的类。
- 一个类可能会实现多个接口,从而让实例扮演多个角色。
- Python语言没有interface关键字,而且除了抽象基类,每个类都有接口:类实现或继承的公开属性(方法或数据属性),包括特殊方法,如__getitem__或__add__。受保护的属性和私有属性不在接口中:即便是“受保护的”属性也只是采用命名约定实现的(单个前导下划线,可以轻松访问)。不要违背这些约定。
1. Python喜欢序列

例子1. 只实现序列协议的一部分,即只定义__getitem__,但这样足够访问元素、迭代和使用in运算符。
鉴于序列协议很重要,如果没有__iter__和__contains__方法,Python会调用__getitem__方法,设法让迭代和in运算符可用。
class Foo:
def __getitem__(self, pos):
return range(0, 30, 10)[pos]
f = Foo()
# 1. f[]调用__getitem__
print(f[1])
# 2. 没有__iter__方法,转而调用__getitem__方法,传入从0开始的整数索引,尝试迭代对象(这是一种后备机制)
for i in f:
print(i)
# 3. 没有__contains__方法,转而调用__getitem__方法
print(20 in f)
Python中的迭代是鸭子类型的一种极端形式:为了迭代对象,解释器会尝试调用两个不同的方法。
2. 使用猴子补丁在运行时实现协议
2.1 如果遵守既定协议,很有可能增加利用现有的标准库的可能性,得益于鸭子类型。例如FrenchDeck实例的行为像序列,就可以用random.shuffle就地地打乱序列。
2.2 猴子补丁:在运行时修改类或模块,而不改动源码。
2.3 例如在FrenchDeck使用猴子补丁之前,虽然部分行为表现得像序列,但仍然不能使用random.shuffle(错误提示为'FrenchDeck' object does not support item assignment)。原因是shuffle函数要调换集合中元素的位置,而FrenchDeck只实现了不可变的序列。可变的序列还必须提供__setitem__方法。
2.4 在运行时修正FrenchDeck为可变序列,让random.shuffle能处理
def set_card(deck, position, card):
deck._cards[position] = card
#猴子补丁,把自定义的函数赋值给__setitem__,把它变成可变的。
FrenchDeck.__setitem__ = set_card
2.5 猴子补丁缺点:打补丁的代码与要打补丁的程序耦合十分紧密,而且往往要处理隐藏和没有文档的部分。(例如这里的关键是set_card函数要知道deck对象有一个名为_cards属性,而且_cards的值必须是可变序列)
2.6 还要注意的是:
3. 直接使用抽象基类,而不只将其当作文档。
3.1 Python的抽象基类有一个重要的实用优势:可以实用register类方法在终端用户的代码中把某个类“声明“为一个抽象基类的”虚拟“子类。做register时,我们保证注册的类忠实地实现了抽象基类定义的接口,而Python会相信我们,从而不做检查。如果我们说谎了,那么常规的运行时异常会把我们捕获。(注册的类不会从抽象基类中继承任何方法或属性)
3.2 有时,为了让抽象基类识别子类,甚至不用注册。无需注册,abc.Sized也能把Struggle识别为自己的子类,只要实现了特殊方法__len__即可。
class Struggle:
def __len__(self):
return 23
from collections import abc
#True
print(isinstance(Struggle(), abc.Sized))
3.3 检查是不是”序列“,那就这样做:
isinstance(the_arg, collections.abc.Sequence)
3.4 最好避免自定义抽象基类。如果自己想创建新的抽象基类,先试着通过常规的鸭子类型来解决问题。
3.5 colletions.namedtuple处理field_names参数的原理
field_names的值可以是单个字符串,以空格或者逗号分隔标识符,也可以是一个标识符序列。
例子3.5.1 用鸭子类型处理单个字符串或由字符串组成的可迭代对象
#1. 假设是单个字符串风格(EAFP风格)
try:
#2. 把逗号替换成空格,然后拆分成名称列表。
field_names =
field_names.replace(',', ' ').split()
#3. 如果field_names看起来不像是字符串
except AttributeError:
#4. 假设已经是由名称组成的可迭代对象
pass
#5. 为了确保的确是可迭代对象,也为了保存一份副本,使用所得值创建一个元组。
field_names = tuple(field_names)
4. !继承抽象基类
例子1. 继承抽象基类,抽象基类会对子类作出限制。
导入时(加载并编译.py模块时),Python不会检查抽象方法的实现。在运行时实例化子类才会真正检查。
例子2. 假如我有一个FrenchDeck2的类继承MutableSequence抽象基类。
例子3. 我们可以覆盖子类中的从抽象基类中继承的方法,以更高效的方式重新实现。
例如,__cotains__方法会全面扫描序列。如果你定义的序列按顺序保存元素,那就可以重新定义__cotains__方法,使用bisect函数做二分查找,从而提升搜索速度。
5. !标准库中的抽象基类
collections.abc中的抽象基类最常用。numbers和io包中也有一些抽象基类。
5.1 collections.abc模块中的抽象基类
- 官方文档:https://docs.python.org/3/library/collections.abc.html
- 简要的UML类图(没有属性名称)



5.2 numbers的抽象基类
- https://docs.python.org/3/library/numbers.html
- numbers包定义的是”数字塔“(各个抽象基类的层次结构是线性的),其中Number是位于最顶端的超类,依次往下,最底端的是Integral类。
Number、Complex、Real、Rational、Integral.- 因此,如果想检查一个数是不是整数,可以使用isinstance(x, numbers.Integral),

6. 定义并使用一个抽象基类
- 最好不要自定义抽象基类
- 此处自定义抽象基类的目的是学会怎么阅读标准库和其他包中的抽象基类源码。
- 中文电子书P491
7. Python使用register
- 当装饰器用:@Sequence.register
- 当作函数用:Sequence.register(tuple)
8. 鹅(本章提到了goose typing)的行为有可能像鸭子
- 即便不注册,抽象基类也能把一个类别识别为虚拟子类。
class Struggle:
def __len__(self):
return 23
from collections import abc
isinstance(Struggle(), abc.Sized)
issubclass(Struggle, abc.Sized)
原理:


Fluent_Python_Part4面向对象,11-iface-abc,协议(接口),抽象基类的更多相关文章
- 【Python】【元编程】【从协议到抽象基类】
"""class Vector2d: typecode = 'd' def __init__(self,x,y): self.__x = float(x) self.__ ...
- Python 接口:从协议到抽象基类
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica } 抽象基类的常见用途:实现接口时作为超类使用.然后,说明抽象基类如何检查 ...
- python 用abc模块构建抽象基类Abstract Base Classes
见代码: #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/08/01 16:58 from abc import ABCMet ...
- Python抽象基类:ABC谢谢你,因为有你,温暖了四季!
Python抽象基类:ABC谢谢你,因为有你,温暖了四季! Python抽象基类:ABC谢谢你,因为有你,温暖了四季! 实例方法.类方法和静态方法 抽象类 具名元组 参考资料 最近阅读了<Pyt ...
- python3:iterable, iterator, generator,抽象基类, itertools的使用。
目录: iterable对象 iterator对象, 数据类型Iterator类 数据类型Generator类. 生成器表达式 collections.abc:容器的抽象基类.用于判断具体类. ite ...
- Python中的抽象基类
1.说在前头 "抽象基类"这个词可能听着比较"深奥",其实"基类"就是"父类","抽象"就是&quo ...
- 6、面向对象以及winform的简单运用(抽象基类与接口)
抽象类与抽象方法 1.书写规范: 在类前面加上abstract关键字,就成为了抽象类:在一个方法前面加上abstract关键字,就成为了抽象方法(抽象方法不能有实现方法,直接在后面加分号) 例: ab ...
- NSObject class和NSObject protocol的关系(抽象基类与协议)
[转载请注明出处] 1.接口的实现 对于接口这一概念的支持,不同语言的实现形式不同.Java中,由于不支持多重继承,因此提供了一个Interface关键词.而在C++中,通常是通过定义抽象基类的方式来 ...
- PythonI/O进阶学习笔记_3.2面向对象编程_python的继承(多继承/super/MRO/抽象基类/mixin模式)
前言: 本篇相关内容分为3篇多态.继承.封装,这篇为第二篇 继承. 本篇内容围绕 python基础教程这段: 在面向对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法.使 ...
随机推荐
- TCP/IP详解,卷1:协议--ARP:地址解析协议
引言 本章我们要讨论的问题是只对 T C P / I P 协议簇有意义的 I P 地址.数据链路如以太网或令牌 环网都有自己的寻址机制(常常为 48 bit 地址),这是使用数据链路的任何网络层都必须 ...
- 我竟然把today = new Date();写在了全局变量里面
let today = new Date();应该在每次用之前重新生成新的对象,因为对于例如 today.getTime() 这种方法,取得是today对象的time,而非调用today对象取得实时时 ...
- javaFx中Image的路径问题
网络图像文件前面加“http://”,而本地文件则要加“file:”.将源代码改为: Image image = new Image("file:image/qq.jpg"); I ...
- springboot+jwt
大概了解下SpringMVC和jwt,百度 代码: 1.整体框架 2.controller package com.yiyezhiqiu.jwt.jwt.controller; import com. ...
- CentOS7更换阿里yum源
更换之前确保自己安装wget yum list wget 若没有安装: yum -y install wget 首先备份原版/etc/yum.repos.d/CentOS-Base.repo cd / ...
- Labview初识
Labview2013安装教程 请访问http://wenku.baidu.com/link?url=Nw4pYpRqMupd9Bn3OfkFBoYM6Hhw9TqWvffZHX-GDQYPCTtqo ...
- bugku 宽带信息泄露
首先下载文件 下载完成后发现是一个后缀名为 bin 的文件 然后找百度查一下这是什么文件的后缀名 看一下题目 然后用软件routerpassview打开(搜的教程) 然后打开文件 搜索username ...
- 吴裕雄 python 机器学习——多维缩放降维MDS模型
# -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as plt from sklearn import datas ...
- 从TCL欲成JDI股东看,面板行业进入“群架”时代
当下,屏幕早已成为第一入口.PC.智能手机.平板电脑.电视.家庭智能终端.智慧交通.智能穿戴设备.汽车中控大屏--种种设备都是以屏幕为最重要的视觉呈现方式,让人们在一个个奇幻世界中畅游.也正是因为屏幕 ...
- Hadoop架构: 关于Recovery (Lease Recovery , Block Recovery, PipeLine Recovery)
该系列总览: Hadoop3.1.1架构体系——设计原理阐述与Client源码图文详解 : 总览 在HDFS中,有三种Recovery 1.Lease Recovery 2.Block Recover ...



