Python Cookbook-6.5 继承的替代方案——自动托管

任务

你需要从某个类或者类型继承,但是需要对继承做一些调整。比如,需要选择性地隐藏某些基类的方法,而继承并不能做到这一点。

解决方案

继承是很方便的,但它并不是万用良药。比如,它无法让你隐藏基类的方法或者属性。而自动托管技术则提供了一种很好的选择。假设需要把一些对象封起来变成只读对象从而避免意外修改的情况。那么,除了禁止属性设置的功能,还需要隐藏修改属性的方法。下面我们给出一个办法:

#同时支持2.3和2.4
try:set
except NameError:from sets import Set as set
class ROError(AttributeError):pass
class Readonly:#这里并没有用继承,我们会在后面讨论其原因mutators = {list:set('''__delitem__ __delslice__ __iadd__ __imul____setitem__ __setslice__ __append extend insert pop remove sort'''.split()),dict:set('''__delitem__ __setitem__ clear pop popitemsetdefault update'''.split()),}def __init__(self,o):object.__setattr__(self,'_o',o)object.__setattr__(self,'_no',self.mutators.get(type(o),()))def __setattr__(self,n,y):raise ROError,"Can't set attr %r on RO obiect" %n def __delattr__(self,n):raise ROError,"Can't del attr %r from Ro object" %n def __getattr__(self,n):if n in self._no:raise ROError,"Can't get attr %r from Ro object" %nreturn getattr(self._o, n)

通过修改 mutators,即 Readonly.mutators[sometype] = the_mutators,还可以轻松地增加其他需要处理的类型。

讨论

自动托管是一种强大而通用的技术。在本节的例子中,通过使用这个技术我们能得到和类继承几乎完全一样的效果,同时还能隐藏一些名字。我们在任务中使用这个模拟的子类将一些对象封装起来,使之变成只读对象。它的性能也许不如真正的继承,但另一方面,作为补偿,我们获得了更好的灵活度和更精细的粒度控制。

基本的想法是,我们的类的每个实例都含有我们想要封装的类型的实例。每当客户代码试图从我们的类的实例中获取属性时,除非该属性已经在类中被定义了(比如定义在 Readonly类的 mutators 字典中),否则__getattr__ 在完成检查之后,会透明地将这个请求转交给被封装的实例。在Python中,方法同样也是属性,访问的方式也一样,所以无论是访问方法还是属性,代码无须改变。用来访问属性的__getattr__方法同时也可用于访问方法。

解决方案的注释没有解释不使用继承的原因,这里我们会给出一点解释。这种基于__getattr__的方式也可用于特殊方法,但仅对旧风格类的实例有效。在新的对象模型中,Python 操作直接通过类的特殊方法来进行,而不是实例的。关于这个问题的更多内容可以在 6.6 节和 20.8节中看到。本节采用的方案——让 Readonly 类成为旧风格类,从而避开这个问题,并把相关内容留到其他章节——在真实的生产代码中是不值得推荐的。我在这里用仅仅是为了控制篇幅,同时避免重复其他章节的内容。

setattr__的角色类似于__getattr,当客户代码设置实例的属性时,它就会被调用,这个任务要求某些属性为只读,我们只需简单地禁止属性访问操作即可。记住,要在方法的代码编写中避免激发对__setattr__的调用,在有__setattr__的类的方法中你不应该使用self.n = v这样的语句。最简单的是直接把设置操作委托给类object,如同类Readonly在它的__init__ 方法中所做的那样。方法__delattr__完成了最后拼图,它会处理那些试图从实例中删除属性的操作。

以自动托管方式完成的封装并不适用于采用了类型检查的客户代码或者框架代码。在那种情况下,客户代码或框架代码完全破坏了多态性,代码本身应该是被重写的。记住不要在你自己的代码中使用类型检查,因为你可能根本无须那么做。见6.13节提供的更好的选择。

在 Python 的老版本中,自动托管的流行程度甚至比现在还高,那是因为当时 Python 不支持从内建的类型继承。而对于现在的 Python,从内建类型继承是允许的,因此自动托管就用得不那么频繁了。不过,自动托管仍然具有它的地位——它只是稍微远离了聚光灯一点点。托管比继承更加灵活,而有时这种灵活是无价的。除了选择性地托管(从而高效地实现了某些属性的“隐藏”),一个对象还可以在不同的时间托管给不同的子对象,或者一次托管给多个子对象,继承无法提供任何能够与之相比的特性。下面给出托管给多个特定子对象的例子。假设你有个类,提供各种“转发方法”,比如:

class Pricing(object):def __init__(self,location,event):self.location = locationself.event = eventdef setlocation(self,location):self.location = locationdef getprice(self):return self.location.getprice()def getquantity(self):return self.location.getquantity()def getdiscount(self):return self.event.getdiscount()and many more such methods

继承很明显不适用,因为 Pricing的实例需要托管给特定的location和event实例,这些实例在初始化阶段传入而且可能会被修改。自动托管的补救方法是:

class AutoDelegator(object):delegates = ()do_not_delegate = ()def __getattr__(self,key):if key not in self.do_not_delegate:for d in self.delegates:try:return getattr(d,key)except AttributeError:passraise AttributeError,key
class Pricing(AutoDelegator):def __init__(self,location,event):self.delegates = [location,event]def setlocation(self,location):self.delegates[0] = location

在此例中,我们没有托管属性的删除和设置,而只是托管了属性的获取(还有一些非特殊方法)。当然,这个方式只有在我们想要托管的各个对象的方法(以及其他属性)不会互相干扰的情况下才会充分有效,比如,location最好不要有个getdiscount方法否则它会抢先进行方法的托管,而此方法原本应该是由event对象来执行的。

如果一个需要大量托管的类涉及这种问题,它可以简单地定义一些对应的方法,这是因为只有在用别的方式无法找到属性和方法时,__getattr__才会介入。而通过do_not_delegate 属性还可以隐藏托管对象的一些属性和方法,而且它也可以被子类改写。举个例子,如果类 Pricing 想要隐藏一个叫做 setdiscount 的方法,此方法由 event提供,做一点点修改就可以了:

class Pricing(AutoDelegator):
do_not_delegate = ('set_discount')

其余部分则与前面代码片段相同。

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

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

相关文章

长短期记忆网络:从理论到创新应用的深度剖析

一、引言 1.1 研究背景 深度学习在人工智能领域的发展可谓突飞猛进,而长短期记忆网络(LSTM)在其中占据着至关重要的地位。随着数据量的不断增长和对时序数据处理需求的增加,传统的神经网络在处理长序列数据时面临着梯度消失和梯…

vue3.2 + element-plus 实现跟随input输入框的弹框,弹框里可以分组或tab形式显示选项

效果 基础用法&#xff08;分组选项&#xff09; 高级用法&#xff08;带Tab栏&#xff09; <!-- 弹窗跟随通用组件 SmartSelector.vue --> <!-- 弹窗跟随通用组件 --> <template><div class"smart-selector-container"><el-popove…

C语言中冒泡排序和快速排序的区别

冒泡排序和快速排序都是常见的排序算法&#xff0c;但它们在原理、效率和应用场景等方面存在显著区别。以下是两者的详细对比&#xff1a; 一、算法原理 1. 冒泡排序 原理&#xff1a;通过重复遍历数组&#xff0c;比较相邻元素的大小&#xff0c;并在必要时交换它们的位置。…

软件信息安全性测试如何进行?有哪些注意事项?

随着信息技术的高速发展&#xff0c;软件已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;随着软件产品的广泛普及&#xff0c;软件信息安全性问题也日益凸显&#xff0c;因此软件信息安全性测试必不可少。那么软件信息安全性测试应如何进行呢?在进行过程中又有哪…

springboot集成mybaits-generator自动生成代码

文章目录 概述创建springboot项目pom文件aplication.yml代码生成类mybatis-plus提供的变量controller模板mapper模板总结 概述 创建springboot项目&#xff0c;在这里使用的是springboot 2.6.13版本&#xff0c;引入的项目依赖包如pom文件所写&#xff0c;jdk使用1.8&#xff…

数据库脱裤

假设你已经getshell 找到mysql账号密码。 网站要连接mysql&#xff0c;就需要把mysql的账号密码保存在一个php文件中&#xff0c;类似config.php、common.inc.php等&#xff0c;在shell中&#xff0c;读取这些文件&#xff0c;找到其中信息即可 下面是一些常见平台的配置文…

leetcode 337. House Robber III

用动态规划的思想解决这道题。 对于每一个节点&#xff0c;只有两种可能&#xff0c;偷或者不偷。 对于一颗以root为根节点的二叉树&#xff0c;定义rob表示偷root节点能从这棵二叉树偷到的最大金额。定义notrob表示不偷root节点能从这棵二叉树偷到的最大金额。 递推公式分析…

ES和MySQL概念对比

基本概念 ES和MySQL都属于数据库&#xff0c;不过各有各的特性&#xff0c;大致使用方法与MySQL类似并无区别。 MySQL&#xff1a;擅长事务持有ACID的特性&#xff0c;确保数据的一致性和安全。 ES&#xff1a;持有倒排索引&#xff0c;适合海量数据搜索和分析。 ES和MySQL如何…

【python】针对Selenium中弹框信息无法定位的问题,以下是综合解决方案及注意事项:

一、常见原因分析 1.1 弹窗类型不匹配 若弹窗为alert&#xff0c;需使用driver.switch_to.alert处理&#xff1b; 若为confirm或prompt&#xff0c;同样适用该方法。 1.2 窗口句柄切换问题 新窗口或弹窗可能开启新句柄&#xff0c;需先通过driver.window_handles切换到对应句…

欧拉服务器操作系统安装MySQL

1. 安装MySQL服务器​​ 1. 更新仓库缓存 sudo dnf makecache2. 安装MySQL sudo dnf install mysql-server2. 初始化数据库​ sudo mysqld --initialize --usermysql3. 启动数据库服务 # 启动服务 sudo systemctl start mysqld# 设置开机自启 sudo systemctl enable mysql…

SQLark:一款国产免费数据库开发和管理工具

SQLark&#xff08;百灵连接&#xff09;是一款面向信创应用开发者的数据库开发和管理工具&#xff0c;用于快速查询、创建和管理不同类型的数据库系统&#xff0c;目前可以支持达梦数据库、Oracle 以及 MySQL。 对象管理 SQLark 支持丰富的数据库对象管理功能&#xff0c;包括…

Spring Boot 中的自动配置原理

2025/4/6 向全栈工程师迈进&#xff01; 一、自动配置 所谓的自动配置原理就是遵循约定大约配置的原则&#xff0c;在boot工程程序启动后&#xff0c;起步依赖中的一些bean对象会自动的注入到IOC容器中。 在讲解Spring Boot 中bean对象的管理的时候&#xff0c;我们注入bean对…

Mysql8配置文件

Mysql8配置文件 修改my.cnf----配置持久化键(persistence key)配置表名不区分大小写 修改my.cnf----配置持久化键(persistence key) MySQL8初始化数据库之前配置好这些变量值&#xff0c;初始化数据库之后可能无法修改这个值。 # 服务端配置 [mysqld] ######## 数据目录和基…

关于系统架构思考,如何设计实现系统的高可用?

绪论、系统高可用的必要性 系统高可用为了保持业务连续性保障&#xff0c;以及停机成本量化&#xff0c;比如在以前的双十一当天如果出现宕机&#xff0c;那将会损失多少钱&#xff1f;比如最近几年Amazon 2021年30分钟宕机损失$5.6M。当然也有成功的案例&#xff0c;比如异地…

【Unity笔记】实现可视化配置的Unity按键输入管理器(按下/长按/松开事件 + UnityEvent绑定)

【Unity笔记】实现可视化配置的Unity按键输入管理器 适用于角色控制、技能触发的Unity按键输入系统&#xff0c;支持UnityEvent事件绑定、长按/松开监听与启用开关 一、引言 在 Unity 游戏开发中&#xff0c;处理键盘输入是最常见的交互方式之一。尤其是角色控制、技能释放、菜…

Fortran 中使用 C_LOC 和 C_F_POINTER 结合的方法来实现不同类型指针指向同一块内存区域

在 Fortran 中&#xff0c;可以使用 C_LOC 和 C_F_POINTER 结合的方法来实现不同类型指针指向同一块内存区域。以下是具体方法和示例&#xff1a; 关键步骤&#xff1a; 获取内存地址&#xff1a;用 C_LOC 获取原始数组的 C 地址。类型转换&#xff1a;用 C_F_POINTER 将地址转…

Spring Boot整合Kafka的详细步骤

1. 安装Kafka 下载Kafka&#xff1a;从Kafka官网下载最新版本的Kafka。 解压并启动&#xff1a; 解压Kafka文件后&#xff0c;进入bin目录。 启动ZooKeeper&#xff1a;./zookeeper-server-start.sh ../config/zookeeper.properties。 启动Kafka&#xff1a;./kafka-server-…

【含文档+PPT+源码】基于微信小程序的学校体育馆操场预约系统的设计与实现

课程简介&#xff1a; 本课程演示的是一款基于微信小程序的学校体育馆操场预约系统的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从…

【Leetcode-Hot100】最大子数组和

题目 解答 class Solution(object):def maxSubArray(self, nums):""":type nums: List[int]:rtype: int"""len_nums len(nums)result -1e5left_fit, right_fit 0, len_nums-1if len_nums 1:return nums[0]sum_left, sum_right 0, 0while r…

txt、Csv、Excel、JSON、SQL文件读取(Python)

txt、Csv、Excel、JSON、SQL文件读取&#xff08;Python&#xff09; txt文件读写 创建一个txt文件 fopen(rtext.txt,r,encodingutf-8) sf.read() f.close() print(s)open( )是打开文件的方法 text.txt’文件名 在同一个文件夹下所以可以省略路径 如果不在同一个文件夹下 ‘…