流畅的Python(十一)-从协议到抽象基类

一、核心要义

主要讨论Python中的`接口`,所谓接口就是类实现或继承的一套公开(按照定义,受保护的属性和私有属性不在接口中)属性和方法,包括特殊方法,如__getitem__或__add__等。Python有两套`规范`接口的方式:

1. 鸭子类型和协议,这种方式对接口的`规范`更多是一种约定俗成,并没有代码层面的强制性。

2. 抽象基类,这种方式对接口的`规范`更明确,也能够更显式地验证子类是否符合设计规范

二、代码示例

1、鸭子模型

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/22 21:52
# @Author  : Maple
# @File    : 00-鸭子模型.py
# @Software: PyCharm"""
鸭子模型:在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由“当前方法和属性的集合”决定在鸭子类型中,关注点在于对象的行为能做什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"游泳"和"散步"方法。
在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"游泳"和"散步"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"游泳"和"散步"
方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名
鸭子类型通常得益于"不"测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用
"""class Duck:def __init__(self,name):self.name = namedef swim(self):print('{}在游泳'.format(self.name))def walk(self):print('{}在散步'.format(self.name))class Chicken:def __init__(self, name):self.name = namedef swim(self):print('{}在游泳'.format(self.name))def walk(self):print('{}在散步'.format(self.name))class Person:def __init__(self,name):self.name = namedef watch(self,pet):pet.swim()pet.walk()if __name__ == '__main__':duck = Duck('唐老鸭')chicken = Chicken('烧鸡')person = Person('Max')# watch中的对象,不用关心是什么类型,只要它具有swim和walk方法即可person.watch(duck)print('****************')person.watch(chicken)

2、序列协议

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/22 19:52
# @Author  : Maple
# @File    : 01-序列协议.py
# @Software: PyCharmclass Person:# 实现序列协议的__getitem__方法# 该对象就具备可迭代(本来应该实现了__iter__方法后才具备)和判断元素是否在对象中(本来应该实现了__contains__方法后才具备)的能力# 这就是协议的强大之处,只要实现了某个协议(此例是序列)的部分方法,该对象就自动具备该类协议对象的功能def __getitem__(self, pos):return list(['Maple','kelly','Max'])[pos]# 实现序列协议的__len__方法# 该对象就能够通过len统计其长度def __len__(self):return 3if __name__ == '__main__':person = Person()# 1. person可迭代for p in person:"""输出MaplekellyMax"""print(p)#2. 可判断某个元素是否在person对象中print('Maple' in person) #True#3.对象长度统计print(len(person)) # 3

3、猴子补丁

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/22 20:05
# @Author  : Maple
# @File    : 02-猴子补丁.py
# @Software: PyCharm"""
如果某个类在最初定义的时候,没有实现协议的部分方法,导致某些功能无法使用
可以在后续 通过“猴子补丁”的方式,添加能够实现该功能的协议方法集合
"""
from random import shuffleclass Cars:# 注意第一个参数self,只是约定俗称的命名,并非必然如此# 唯一明确的只是它代表的含义:类的实例化对象def __init__(self,items):self.items = list(items)# 实现__getitem__方法,cars对象可迭代,但cars对象集不能被更改def __getitem__(self, pos):return self.items[pos]def __len__(self):return len(self.items)if __name__ == '__main__':# 1. cars对象迭代cars = Cars(['奔驰','宝马','奥迪'])print([car for car in cars]) # ['奔驰', '宝马', '奥迪']# 尝试随机化cars,报错原因是Cars类没有实现序列协议中的__setitem__方法,所以Cars目前是不可变的序列#shuffle(cars) #'Cars' object does not support item assignment# 2.为Cars动态添加__setitem__方法def set_car(self,key,value):self.items[key] = valueCars.__setitem__ = set_car# 此时就可以对cars进行shuffleshuffle(cars)print(list(cars)) #['奥迪', '奔驰', '宝马']

4、定义抽象基类的子类

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/23 7:01
# @Author  : Maple
# @File    : 03-定义抽象基类的子类.py
# @Software: PyCharmimport collections
from collections.abc import MutableSequenceCard = collections.namedtuple('Card',['rank','suit'])
class FrenchDeck(MutableSequence):"""MutableSequence抽象基类有三个抽象方法,子类必须要实现1. __setitem__2. __delitem__3. insert"""ranks = [str(n) for n  in range(2,11)] +  list('JQKA')suits = 'spades diamonds clubs hearts'.split(' ')def __init__(self):self._cards = [Card(rank,suit) for rank in self.ranksfor suit in self.suits]def __len__(self):return len(self._cards)def __getitem__(self, position):return self._cards[position]def __setitem__(self, key, value):self._cards[key] = valuedef __delitem__(self, position):del self._cards[position]def insert(self,position,value):self._cards.insert(position,value)if __name__ == '__main__':# 1.通过切片获取元素deck = FrenchDeck()print(deck[0])#2.插入元素deck.insert(0,Card('0','spades'))print(deck[0])   # Card(rank='0', suit='spades')#3.删除元素#  删除第二步添加的元素del deck[0]print(deck[0]) # Card(rank='2', suit='spades')

5、自定义抽象基类

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/23 20:00
# @Author  : Maple
# @File    : 04-自定义抽象基类.py
# @Software: PyCharm
import abc# 定义抽象基类
import randomclass Tombola(abc.ABC):@abc.abstractmethoddef load(self,iterable):"""从可迭代对象中加载元素"""@abc.abstractmethoddef pick(self):"""随机删除元素,然后将其返回如果实例为空,这个方法应该抛出LookupError"""def loaded(self):"""如果至少有一个元素,则返回True,否则返回False"""return bool(self.inspect())def inspect(self):"""返回一个有序元组,由当前元素构成"""items = []while True:try:items.append(self.pick())except LookupError:breakself.load(items)return tuple(sorted(items))# 定义一个Tombola基类的测试子类:证明如果子类未实现基类的全部抽象方法,将无法实例化(虽然issubclass仍然为True)
class Fake(Tombola):"""该类继承抽象基类Tombola,同时并没有实现全部的抽象方法,该类实例化会报错"""def load(self,iterable):self.items = list(iterable)def pick(self):pass# Tombola的第一个子类
class BingCage(Tombola):def __init__(self,items):self._randomizer = random.SystemRandom()self._items = []self.load(items)def load(self,items):self._items.extend(items)self._randomizer.shuffle(self._items)def pick(self):try:return self._items.pop()except IndexError:raise LookupError('pick from empty BingCage')# 实现对象可直接调用def __call__(self):self.pick()class LotteryBlower(Tombola):def __init__(self,iterable):# 通过list创建一个iterable的拷贝,从而对balls的操作,不会影响到传递进来的对象self._balls = list(iterable)def load(self,iterable):self._balls.extend(iterable)def pick(self):try:position = random.randrange(len(self._balls))return self._balls[position]except ValueError:raise LookupError('pick from empty LotteryBlower')def loaded(self):return bool(self._balls)def inspect(self):return tuple(sorted(self._balls))if __name__ == '__main__':print(issubclass(Fake,Tombola)) # True#1.如果Fake没有实现全部抽象方法, 实例化会报错print('*************1.如果Fake没有实现全部抽象方法, 实例化会报错**************')fake = Fake() #  Can't instantiate abstract class Fake with abstract methods pick# 2.如果Fake实现了全部的抽象方法, 就能够正常实例化print('*************2.如果Fake实现了全部的抽象方法, 就能够正常实例化**************')fake = Fake()# 3.BingCage测试print('*************3.BingCage测试**************')bingcage = BingCage([1,2,3])# 返回元素print(bingcage.pick()) # 2print(bingcage.pick()) # 1print(bingcage.pick()) # 3#print(bingcage.pick())  # LookupError: pick from empty BingCage# 4.LotteryBlower测试print('*************4.LotteryBlower测试**************')lotter = LotteryBlower(['Maple','Kelly'])print(lotter.pick()) # Kelly

6、虚拟子类

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/23 20:52
# @Author  : Maple
# @File    : 05-虚拟子类.py
# @Software: PyCharm
import abc
from random import randrangeclass Tombola(abc.ABC):@abc.abstractmethoddef load(self,iterable):"""从可迭代对象中加载元素"""@abc.abstractmethoddef pick(self):"""随机删除元素,然后将其返回如果实例为空,这个方法应该抛出LookupError"""def loaded(self):"""如果至少有一个元素,则返回True,否则返回False"""return bool(self.inspect())def inspect(self):"""返回一个有序元组,由当前元素构成"""items = []while True:try:items.append(self.pick())except LookupError:breakself.load(items)return tuple(sorted(items))@Tombola.register
class TomboList(list):# 继承list"""虚拟子类在代码层面,可以不用重写 基类的任何方法,但issubclass和isinstance仍然会判断为真--所以全靠自觉 去实现基类的方法"""def pick(self):if self:position = randrange(len(self))# pop方法继承自listreturn self.pop(position)# load方法load = list.extenddef loaded(self):return bool(self)def insert(self):return tuple(sorted(self))# 注册虚拟类 除了像上面那样使用装饰器,还可以使用如下方式(更通用):
# Tombola.register(TomboList)if __name__ == '__main__':# 1.虚拟子类测试print(issubclass(TomboList,Tombola)) # Truet_list = TomboList([1,2,3])# 2.虚拟子类实例测试print(isinstance(t_list,Tombola)) # True# 3.虚拟子类继承关系列表只会列出`真实`的超类(如下Tombola并不在列表里)#  TomboList继承list,然后list继承objectprint(TomboList.__mro__) # (<class '__main__.TomboList'>, <class 'list'>, <class 'object'>)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/698818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几种后端开发中常用的语言。

几种后端开发中常用的语言。 C/C 语言 C 语言最初是用于系统开发工作&#xff0c;特别是组成操作系统的程序。由于 C 语言所产生的代码运行速度与汇编语言编写的代码运行速度几乎一样&#xff0c;所以采用 C 语言作为系统开发语言。目前&#xff0c;C 语言是最广泛使用的系统…

MongoDB聚合运算符:$atan2

$atan2用来计算反正切&#xff0c;返回指定表达式的反正切值&#xff0c;与$antan的区别主要是参数不同。 语法 { $atan2: [<expression1>, <expression1>] }<expression>为可被解析为数值的表达式$atan2返回弧度&#xff0c;使用$radiansToDegrees运算符可…

数据结构与算法-常用排序算法

一、常用排序说明 当涉及排序算法时&#xff0c;理解每个算法的工作原理、时间复杂度和空间复杂度是至关重要的。下面对常用排序算法进行详细说明&#xff1a; 1、冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a; 工作原理&#xff1a;比较相邻的元素并交换&am…

python bug与debug

一、什么是bug&#xff08;软件缺陷&#xff09;&#xff1f; 产品说明书中规定要做的事情&#xff0c;而软件没有实现。 产品说明书中规定不要做的事情&#xff0c;而软件确实现了。 产品说明书中没有提到过的事情&#xff0c;而软件确实现了。 产品说明书中没有提到但是必…

跨语言的序列化与反序列化

在Java中实现跨语言的序列化与反序列化通常可以采用以下几种方式 使用标准的跨语言序列化格式 可以选择使用一些标准的跨语言序列化格式,例如JSON、XML、Protocol Buffers(ProtoBuf)等。这些格式都是跨语言的,可以方便地在不同的编程语言之间进行数据交换。在Java中,可以…

紫光同创初使用

芯片PGC2KG-6LPG144 1、安装好软件接&#xff0c;加载license,有两个&#xff0c;与电脑MAC地址绑定的 2、正常使用后&#xff0c;新建个工程&#xff0c;配置管脚Tools→UCE 3、程序中有些信号被软件认为是时钟信号&#xff0c;会报错&#xff08;时钟输入I0约束在非专用时钟…

LeetCode--代码详解 78.子集

78.子集 题目 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[],[1…

Python爬虫-使用代理伪装IP

爬虫系列&#xff1a;http://t.csdnimg.cn/WfCSx 前言 我们在做爬虫的过程中经常会遇到这样的情况&#xff0c;最初爬虫正常运行&#xff0c;正常抓取数据&#xff0c;一切看起来都是那么的美好&#xff0c;然而一杯茶的功夫可能就会出现错误&#xff0c;比如 403 Forbidden&…

【LeetCode刷题笔记】242.有效的字母异位词

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

Spring基础之AOP和代理模式

文章目录 理解AOPAOP的实现原理 AOP代理模式静态代理动态代理1-JDK动态代理2-CGLIB动态代理 总结 理解AOP OOP - - Object Oriented Programming 面向对象编程 AOP - - Aspect Oriented Programming 面向切面编程 AOP是Spring提供的关键特性之一。AOP即面向切面编程&#xff0…

Jenkins邮件通知配置(7)

1、安装插件&#xff1a; Email Extension&#xff0c;Email Extension Template&#xff0c;这两个插件可以帮助我们进行邮件的编写发送以及格式化 2、配置jenkins中链接腾讯企业邮箱 先配置发送服务&#xff0c;然后在具体工程中设置接收者 基础信息&#xff1a; POP3/S…

SWIFT:自我认知微调

文档:https://github.com/modelscope/swift/blob/main/docs/source/LLM/%E8%87%AA%E6%88%91%E8%AE%A4%E7%9F%A5%E5%BE%AE%E8%B0%83%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5.md ​​​​​​代码: Swift是如何把自我认知数据集融合到训练集中呢? 1:相关的3个参数

设计模式学习笔记 - 面向对象 - 6.为什么要基于接口而非实现编程?有必要为每个类都定义接口吗?

前言 “基于接口而非实现编程”这个原则非常重要&#xff0c;是一种非常有效的提高代码质量的手段&#xff0c;在平时的开发中经常被用到。 如何解读原则中的“接口”二字 要理解“基于接口而非实现编程”的关键就是要理解其中的“接口”二字&#xff0c;我们可以理解为编程语…

学习数据节构和算法的第14天

题目讲解 链表的移除 #include <stdio.h> #include <stdlib.h> // 定义链表节点结构体 typedef struct Node {int data; // 节点数据struct Node* next; // 指向下一个节点的指针 } Node; // 初始化链表节点 Node* initNode(int data) {Node* n…

mfc 疑难杂症之一

病情&#xff1a; 1.xxxx处的第一机会异常: 0xC0000005: 读取位置 0x00000004 时发生访问冲突。 2.不定时程序闪退 访问违例 程序定位到 main处 0x76DCB5B2 处(位于 Tetris.exe 中)引发的异常: Microsoft C 异常: CResourceException&#xff0c;位于内存位置 0x008FF0D4 处…

vue计算属性和监听器详解

1.watch 和 computed 的作用和区别 watch&#xff08;侦听器&#xff09; 作用&#xff1a; 监听器允许开发者自定义一个函数来观察 Vue 实例上的特定数据属性的变化&#xff0c;当这些属性发生变化时&#xff0c;会触发相应的回调函数。 特点&#xff1a; 非缓存&#xff1a…

用支持向量机进行光学符号识别

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

企业级大数据安全架构(十一)Kerberos接入dophinscheduler

作者&#xff1a;楼高 建议将dophinscheduler集成到Ambari安装部署&#xff0c;在Ambari上面开启kerberos 1.安装准备 编译 从GitHub获取dolphinscheduler-1.3.9源码 git clone https://github.com/apache/dolphinscheduler.git -b 1.3.9-releasehttps://github.com/apache/…

多输入回归预测|GWO-CNN-LSTM|灰狼算法优化的卷积-长短期神经网络回归预测(Matlab)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 灰狼优化算法&#xff1a; 卷积神经网络-长短期记忆网络&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容…

java日志框架总结(七、使用过滤器自动打印接口入参、出参)

使用过滤器自动打印接口入参、出参首先要了解一个过滤器OncePerRequestFilter&#xff0c;一般使用这个过滤器进行日志打印。 一、OncePerRequestFilter 1)、什么是OncePerRequestFilter 回顾一下 Filter 的工作原理。Filter 可以在 Servlet 执行之前或之后调用。当请求被调度…