位置: 编程技术 - 正文

Python 的描述符 descriptor详解(python文件描述符)

编辑:rootadmin

推荐整理分享Python 的描述符 descriptor详解(python文件描述符),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:python文件描述符,python 描述器 详解,python描述符的作用,python字符串描述,python描述符的作用,python描述性统计代码,python文件描述符,python文件描述符,内容如对您有帮助,希望把文章链接给更多的朋友!

Python 在 2.2 版本中引入了descriptor(描述符)功能,也正是基于这个功能实现了新式类(new-styel class)的对象模型,同时解决了之前版本中经典类 (classic class) 系统中出现的多重继承中的 MRO(Method Resolution Order) 问题,另外还引入了一些新的概念,比如 classmethod, staticmethod, super, Property 等。因此理解 descriptor 有助于更好地了解 Python 的运行机制。

那么什么是 descriptor 呢?

简而言之:descriptor 就是一类实现了__get__(), __set__(), __delete__()方法的对象。

Orz...如果你瞬间顿悟了,那么请收下我的膝盖;O_o!...如果似懂非懂,那么恭喜你!说明你潜力很大,咱们可以继续挖掘:

引言

对于陌生的事物,一个具体的栗子是最好的学习方式,首先来看这样一个问题:假设我们给一次数学考试创建一个类,用于记录每个学生的学号、数学成绩、以及提供一个用于判断是否通过考试的check 函数:

很简单一个示例,看起来运行的不错:

但是会有一个问题,比如手一抖录入了一个负分数,那么他就得悲剧的挂了:

这显然是一个严重的问题,怎么能让一个数学 + 的孩子挂科呢,于是乎一个简单粗暴的方法就诞生了:

上面再类的初始化函数中增加了负数判断,虽然不够优雅,甚至有点拙劣,但这在实例初始化时确实工作的不错:

OK, 但我们还无法阻止实例对 score 的赋值操作,毕竟修改成绩也是常有的事:

对于大多数童鞋,这个问题 so easy 的啦:将 score 变为私有,从而禁止 xiaoming.score 这样的直接调用,增加一个 get_score 和 set_score 用于读写:

这确实是种常见的解决方法,但是不得不说这简直丑爆了:

调用成绩再也不能使用 xiaoming.score 这样自然的方式,需要使用 xiaoming.get_score() ,这看起来像口吃在说话!还有那反人类的下划线和括号...那应该只出现在计算机之间窃窃私语之中...赋值也无法使用 xiaoming.score = , 而需使用 xiaoming.set_score(), 这对数学老师来说,太 TM 不自然了 !!!

作为一门简洁优雅的编程语言,Python 是不会坐视不管的,于是其给出了 Property 类:

Property 类

先不管 Property 是啥,咱先看看它是如何简洁优雅的解决上面这个问题的:

与上段代码相比,主要是在最后一句实例化了一个 property 实例,并取名为 score, 这个时候,我们就能如此自然的对 instance.__score 进行读写了:

WOW~~一切工作正常!嗯,那么问题来了:它是怎么工作的呢?先看下 property 的参数:

class property(fget=None, fset=None, fdel=None, doc=None) #拷贝自 Python 官方文档它的工作方式:

实例化 property 实例(我知道这是句废话);调用 property 实例(比如xiaoming.score)会直接调用 fget,并由 fget 返回相应值;对 property 实例进行赋值操作(xiaoming.score = )则会调用 fset,并由 fset 定义完成相应操作;删除 property 实例(del xiaoming),则会调用 fdel 实现该实例的删除;doc 则是该 property 实例的字符说明;fget/fset/fdel/doc 需自定义,如果只设置了fget,则该实例为只读对象;这看起来和本篇开头所说的 descriptor 的功能非常相似,让我们回顾一下 descriptor:

“descriptor 就是一类实现了__get__(), __set__(), __delete__()方法的对象。”

@~@ 如果你这次又秒懂了,那么请再次收下我的膝盖 Orz...

另外,Property 还有个装饰器语法糖 @property,其所实现的功能与 property() 完全一样:

我们知道了 property 实例的工作方式了,那么问题又来了:它是怎么实现的?事实上 Property 确实是基于 descriptor 而实现的,下面进入我们的正题 descriptor 吧!

Python 的描述符 descriptor详解(python文件描述符)

descriptor 描述符

照样先不管 descriptor 是啥,咱们还是先看栗子,对于上面 Property 实现的功能,我们可以通过自定义的 descriptor 来实现:

我们新定义了一个 NonNegative 类,并在其内实现了__get__、__set__方法,然后在 MathScore 类中实例化了一个 NonNegative 的实例 score,注意!!!重要的事情说三遍:score 实例是 MathScore 的类属性!!!类属性!!!类属性!!!这个 Mathscore.score 属性同上面 Property 的 score 实例的功能是一样的,只不过 Mathscore.score 调用的 get、set 并不定义在 Mathscore 内,而是定义在 NonNegative 类中,而 NonNegative 类就是一个 descriptor 对象!

纳尼? NonNegative 类的定义中可没见到半个 “descriptor” 的字样,怎么就成了 descriptor 对象???

淡定! 重要的事情这里只说一遍:任何实现 __get__,__set__ 或 __delete__ 方法中一至多个的类,就是 descriptor 对象。所以 NonNegative 自然是一个 descriptor 对象。

那么 descriptor 对象与普通类比有什么特别之处呢? 先不急,来看看上端代码的效果:

可以发现,MathScore.score 虽然是一个类属性,但它却可以通过实例的进行赋值,且面对不同的 MathScore 实例 xiaoming、wangerma 的赋值和调用,并不会产生冲突!因此看起来似乎更类似于 MathScore 的实例属性,但与实例属性不同的是它并不通过 MathScore 实例的读写方法操作值,而总是通过 NonNegative 实例的 __get__ 和 __set__ 对值进行操作,那么它是怎么做到这点的?

注意看 __get__、__set__ 的参数

def __get__(self, ist, cls): #self:descriptor 实例本身(如 Math.score),ist:调用 score 的实例(如 xiaoming),cls:descriptor 实例所在的类(如MathScore) ...

def __set__(self, ist, value): #score 就是通过这些传入的 ist 、cls 参数,实现对 MathScore 及其具体实例属性的调用和改写的 ...OK, 现在我们基本搞清了 descriptor 实例是如何实现对宿主类的实例属性进行模拟的。事实上 Property 实例的实现方式与上面的 NonNegative 实例类似。那么我们既然有了 Propery,为什么还要去自定义 descriptor 呢?

答案在于:更加逼真的模拟实例属性(想想 MathScore.__init__里面那恶心的判断语句),还有最重要的是:代码重用!!!

简而言之:通过单个 descriptor 对象,可以更加逼真的模拟实例属性,并且可以实现对宿主类实例的多个实例属性进行操作。

O.O! 如果你又秒懂了,那么你可以直接跳到下面写评论了...

看个栗子:假如不仅要判断学生的分数是否为负数,而且还要判学生的学号是否为负值,使用 property 的实现方式是这样子的:

Property 实例最大的问题是:

无法影响宿主类实例的初始化,所以咱必须在__init__ 加上那丑恶的 if ...单个 Property 实例仅能针对宿主类实例的单个属性,如果需要对多个属性进行控制,则必须定义多个 Property 实例, 这真是太蛋疼了!但是自定义 descriptor 可以很好的解决这个问题,看下实现:

哈哈~! MathScore.__init__ 内终于没了 if ,代码也比上面的简洁不少,但是功能一个不少,且实例之间不会相互影响:

事实上,MathScore 多个实例的同一个属性,都是通过单个 MathScore 类的相应类属性(也即 NonNegative 实例)操作的,这同 property 一致,但它又是怎么克服 Property 的两个不足的呢?秘诀有三个:

Property 实例本质上是借助类属性,变向对实例属性进行操作,而 NonNegative 实例则是完全通过类属性模拟实例属性,因此实例属性其实根本不存在;

NonNegative 实例使用字典记录每个 MathScore 实例及其对应的属性值,其中 key 为 MathScore 实例名:比如 score 实例就是使用 dic = {‘Zhangsan':, ‘Lisi':} 记录每个实例对应的 score 值,从而确保可以实现对 MathScore 实例属性的模拟;MathScore 通过在__init__内直接调用类属性,从而实现对实例属性初始化赋值的模拟,而 Property 则不可能,因为 Property 实例(也即MathScore的类属性)是真实的操作 MathScore 实例传入的实例属性以达到目的,但如果在初始化程序中传入的不是实例属性,而是类属性(也即 Property 实例本身),则会陷入无限递归(PS:想一下如果将前一个property 实例实现中的self.__score 改成这里的 self.score 会发生什么)。

这三点看的似懂非懂,没关系,来个比喻:

每个 descriptor 实例(MathScore.score 和 MathScore.std_id)都是类作用域里的一个篮子,篮子里放着写着每个 MathScore 实例名字的盒子(‘zhangsan','lisi‘),同一个篮子里的盒子只记录同样属性的值(比如score篮子里的盒子只记录分数值),当 MathScore 的实例对相应属性进行操作时,则找到对应的篮子,取出标有该实例名字的盒子,并对其进行操作。

因此,实例对应的属性,压根不在实例自己的作用域内,而是在类作用域的篮子里,只不过我们可以通过 xiaoming.score 这样的方式进行操作而已,所以其实际的调用的逻辑是这样的:下图右侧的实例分别通过红线和黑线对score和std_id 进行操作,他们首先通过类调用相应的类属性,然后类属性通过对应的 descriptor 实例作用域对操作进行处理,并返回给类属性相应结果,最后让实例感知到。

看到这里,很多童鞋可能不淡定了,因为大家都知道在 Python 中采取 xiaoming.score = 这样的赋值方式,如果 xiaoming 没有 score 这样的实例属性,必定会自动创建该实例属性,怎么会去调用 MathScore 的 score 呢?

首先,要鼓掌!!! 给想到这点的童鞋点赞!!!其实上面在说 Property 的时候这个问题就产生了。

其次,Python 为了实现 discriptor 确实对属性的调用顺序做出了相应的调整,这些将会“Python 的 descriptor(下)”中介绍。

Python 多线程抓取图片效率对比 目的:是学习python多线程的工作原理,及通过抓取张图片这种IO密集型应用来查看多线程效率对比importrequestsimporturlparseimportosimporttimeimportthreadingimportQueu

如何在Python中编写并发程序 GIL在Python中,由于历史原因(GIL),使得Python中多线程的效果非常不理想.GIL使得任何时刻Python只能利用一个CPU核,并且它的调度算法简单粗暴:多线程中,让每个

Python实现简单多线程任务队列 最近我在用梯度下降算法绘制神经网络的数据时,遇到了一些算法性能的问题。梯度下降算法的代码如下(伪代码):defgradient_descent():#thegradientdescentcod

标签: python文件描述符

本文链接地址:https://www.jiuchutong.com/biancheng/383333.html 转载请保留说明!

上一篇:简析Python的闭包和装饰器(python中闭包的作用)

下一篇:Python 多线程抓取图片效率对比(python多线程操作)

  • 个人所得税起征点2023税率表
  • 没有参加会计继续教育会怎样
  • 珠宝首饰可以开发票吗
  • 综合所得申报怎么作废
  • 长期股权投资为什么要冲减资本公积
  • 无形资产摊销计算例题
  • 长期利润分享计划属于短期薪酬吗
  • 市政押金无法收回的损失可以税前扣除吗
  • 基本户购买支票需要什么
  • 工程结算会计科目及账务处理
  • 土地使用权评估原则
  • 专票红冲账务处理
  • 在途物资的运费放在哪个科目
  • 接受捐赠材料支付的运费 扣除
  • 非居民工资薪金个人所得税计算方法
  • 冲回存货跌价准备计提递延所得税
  • 购买资产佣金应该怎么算
  • 补充协议如何生效
  • 不含税劳务报酬怎么交税的
  • 专项产资金支出要怎么做分录?
  • 暂估入库冲回的会计分录
  • 收回员工社保入什么科目
  • 环保公司开具发票怎么开
  • 上月未申报纳税补申报怎么做不会有罚款吗?
  • 免抵退税额抵减额是什么意思
  • 服务费与劳务费税率的区别
  • 工程施工纳税
  • 中小企业划分标准
  • 应交税费进项税额在借方说明什么
  • 多结转成本的会计分录
  • 同一张发票可以分两次报销吗
  • 一次性所得个人所得税缴纳标准
  • 社保包括哪些保险及比例
  • 微信红包如何发出去
  • ChatGPT解开了我一直以来对自动化测试的疑惑
  • php判断文件是否存在的函数
  • php代理访问
  • 股东投资款超过注册资金的案例
  • 增值税申报表中销售额
  • 小规模纳税人的企业所得税怎么算
  • 营业收入和营业外收入的区别
  • 基于vue的开源甘特图控件
  • 钢筋增值税专票几个点
  • 印花税购销合同包括哪些
  • 红字更正法适用于更正什么样的错误
  • 建筑租赁公司是什么意思
  • 职工福利按工资的实付还是应付提取
  • 没有认证抵扣的发票如何冲红
  • 收到三代手续费如何入账
  • 会计手工做账的目的
  • 公司向股东个人借款怎么做账
  • 产品检验费怎样计算
  • 委托加工物资的加工费计入什么科目
  • 代扣代缴的个人所得税在现金流量表
  • 股东分红入什么科目利润分配~应付利润
  • 分公司第二季度总结报告
  • 会计账簿的扉页只需填列账簿启用日期和经管人员一览表
  • 备查账的登记主要以什么来表述
  • win7系统怎么对外发送wifi信号
  • win8打开桌面
  • Vista 优化预读文件设置,提速开关机速度
  • vnc远程桌面linux
  • Windows任务计划程序服务
  • win10 win8.1双系统
  • linux ftp用户存在哪个文件中
  • win 7怎么设置开机启动
  • linux创建.c
  • javascriptcall和apply
  • ajax请求数据放到页面
  • css鼠标移入显示
  • unity服务器端
  • linux同名文件
  • 菜鸟 javascript
  • rsa加密算法python
  • python如何安装各种库
  • [置顶] [Android Studio 权威教程]最实用的快捷键
  • python html转markdown
  • 国家税务稽查局举报中心
  • 南京税务登记查询官网
  • 工商登记是实质性的吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设