1、什么是描述符?

python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 __get__(), __set__(), 和__delete__()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。

以上为官方定义,纯粹为了装逼使用,一般人看这些定义都有一种问候祖先的冲动!

没关系,看完本文,你就会理解什么叫描述符了!

2、讲解描述符前,先看一下属性:__dict__ (每个对象均具备该属性)

作用:字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}

对象属性的访问顺序:

①.实例属性

②.类属性

③.父类属性

④.__getattr__()方法

以上顺序,切记切记!

1 classTest(object):2 cls_val = 1

3 def __init__(self):4 self.ins_val = 10

5

6

7 >>> t=Test()8 >>> Test.__dict__

9 mappingproxy({'__module__': '__main__', 'cls_val': 1, '__init__': , '__dict__': , '__weakref__': , '__doc__': None})10 >>> t.__dict__

11 {'ins_val': 10}12

13 >>> type(x)==X14 True15

16 #更改实例t的属性cls_val,只是新增了该属性,并不影响类Test的属性cls_val

17 >>> t.cls_val = 20

18 >>> t.__dict__

19 {'ins_val': 10, 'cls_val': 20}20 >>> Test.__dict__

21 mappingproxy({'__module__': '__main__', 'cls_val': 1, '__init__': , '__dict__': , '__weakref__': , '__doc__': None})22

23 #更改了类Test的属性cls_val的值,由于事先增加了实例t的cls_val属性,因此不会改变实例的cls_val值(井水不犯河水)

24 >>> Test.cls_val = 30

25 >>> t.__dict__

26 {'ins_val': 10, 'cls_val': 20}27 >>> Test.__dict__

28 mappingproxy({'__module__': '__main__', 'cls_val': 30, '__init__': , '__dict__': , '__weakref__': , '__doc__': None})

从以上代码可以看出,实例t的属性并不包含cls_val,cls_val是属于类Test的。

3、魔法方法:__get__(), __set__(), __delete__()

方法的原型为:

① __get__(self, instance, owner)

② __set__(self, instance, value)

③ __del__(self, instance)

那么以上的 self, instance owner到底指社么呢?莫急莫急,听我慢慢道来!

首先我们先看一段代码:

1 #代码 1

2

3 classDesc(object):4

5 def __get__(self, instance, owner):6 print("__get__...")7 print("self : \t\t", self)8 print("instance : \t", instance)9 print("owner : \t", owner)10 print('='*40, "\n")11

12 def __set__(self, instance, value):13 print('__set__...')14 print("self : \t\t", self)15 print("instance : \t", instance)16 print("value : \t", value)17 print('='*40, "\n")18

19

20 classTestDesc(object):21 x =Desc()22

23 #以下为测试代码

24 t =TestDesc()25 t.x26

27 #以下为输出信息:

28

29 __get__...30 self : <__main__.desc object at>

31 instance : <__main__.testdesc object at>

32 owner :

33 ========================================

可以看到,实例化类TestDesc后,调用对象t访问其属性x,会自动调用类Desc的 __get__方法,由输出信息可以看出:

① self: Desc的实例对象,其实就是TestDesc的属性x

② instance: TestDesc的实例对象,其实就是t

③ owner: 即谁拥有这些东西,当然是 TestDesc这个类,它是最高统治者,其他的一些都是包含在它的内部或者由它生出来的

到此,我可以揭开小小的谜底了,其实,Desc类就是是一个描述符(描述符是一个类哦),为啥呢?因为类Desc定义了方法 __get__, __set__.

所以,某个类,只要是内部定义了方法 __get__, __set__, __delete__ 中的一个或多个,就可以称为描述符(^_^,简单吧)

说到这里,我们的任务还远远没有完成,还存在很多很多的疑点?

问题1. 为什么访问 t.x的时候,会直接去调用描述符的 __get__() 方法呢?

答:t为实例,访问t.x时,根据常规顺序,

首先:访问Owner的__getattribute__()方法(其实就是 TestDesc.__getattribute__()),访问实例属性,发现没有,然后去访问父类TestDesc,找到了!

其次:判断属性 x 为一个描述符,此时,它就会做一些变动了,将 TestDesc.x 转化为 TestDesc.__dict__['x'].__get__(None, TestDesc) 来访问

然后:进入类Desc的 __get__()方法,进行相应的操作

问题2. 从上面 代码1 我们看到了,描述符的对象 x 其实是类 TestDesc  的类属性,那么可不可以把它变成实例属性呢?

答:我说了你不算,你说了也不算,解释器说了算,看看解释器怎么说的。

1 #代码 2

2

3 classDesc(object):4 def __init__(self, name):5 self.name =name6

7 def __get__(self, instance, owner):8 print("__get__...")9 print('name =',self.name)10 print('='*40, "\n")11

12 classTestDesc(object):13 x = Desc('x')14 def __init__(self):15 self.y = Desc('y')16

17 #以下为测试代码

18 t =TestDesc()19 t.x20 t.y21

22 #以下为输出结果:

23 __get__...24 name =x25 ========================================

咦,为啥没打印 t.y 的信息呢?

因为没有访问 __get__() 方法啊,哈哈,那么为啥没有访问 __get__() 方法呢?(问题真多)

因为调用 t.y 时刻,首先会去调用TestDesc(即Owner)的 __getattribute__() 方法,该方法将 t.y 转化为TestDesc.__dict__['y'].__get__(t, TestDesc), 但是呢,实际上 TestDesc 并没有 y这个属性,y 是属于实例对象的,所以,只能忽略了。

问题3. 如果 类属性的描述符对象 和 实例属性描述符的对象 同名时,咋整?

答:还是让解释器来解释一下吧。

1 #代码 3

2

3 classDesc(object):4 def __init__(self, name):5 self.name =name6 print("__init__(): name =",self.name)7

8 def __get__(self, instance, owner):9 print("__get__() ...")10 returnself.name11

12 def __set__(self, instance, value):13 self.value =value14

15 classTestDesc(object):16 _x = Desc('x')17 def __init__(self, x):18 self._x =x19

20

21 #以下为测试代码

22 t = TestDesc(10)23 t._x24

25 #输入结果

26 __init__(): name =x27 __get__() ...

不对啊,按照惯例,t._x 会去调用 __getattribute__() 方法,然后找到了 实例t 的 _x 属性就结束了,为啥还去调用了描述符的 __get__() 方法呢?

这就牵扯到了一个查找顺序问题:当Python解释器发现实例对象的字典中,有与描述符同名的属性时,描述符优先,会覆盖掉实例属性。

不信?来看一下 字典 :

1 >>> t.__dict__

2 {}3

4 >>> TestDesc.__dict__

5 mappingproxy({'__module__': '__main__', '_x': <__main__.desc object at>, '__init__': , '__dict__': , '__weakref__': , '__doc__': None})

怎么样,没骗你吧?我这人老好了,从来不骗人!

我们再将 代码3 改进一下, 删除 __set__() 方法试试看会发生什么情况?

1 #代码 4

2

3 classDesc(object):4 def __init__(self, name):5 self.name =name6 print("__init__(): name =",self.name)7

8 def __get__(self, instance, owner):9 print("__get__() ...")10 returnself.name11

12 classTestDesc(object):13 _x = Desc('x')14 def __init__(self, x):15 self._x =x16

17

18 #以下为测试代码

19 t = TestDesc(10)20 t._x21

22 #以下为输出:

23 __init__(): name = x

我屮艸芔茻,咋回事啊?怎么木有去 调用 __get__() 方法?

其实,还是 属性 查找优先级惹的祸,只是定义一个 __get__() 方法,为非数据描述符,优先级低于实力属性的!!

问题4. 什么是数据描述符,什么是非数据描述符?

答:一个类,如果只定义了 __get__() 方法,而没有定义 __set__(), __delete__() 方法,则认为是非数据描述符; 反之,则成为数据描述符

问题5. 天天提属性查询优先级,就不能总结一下吗?

答:好的好的,客官稍等!

① __getattribute__(), 无条件调用

② 数据描述符:由 ① 触发调用 (若人为的重载了该 __getattribute__() 方法,可能会调职无法调用描述符)

③ 实例对象的字典(若与描述符对象同名,会被覆盖哦)

④ 类的字典

⑤ 非数据描述符

⑥ 父类的字典

⑦ __getattr__() 方法

python描述符详解_Python描述符 (descriptor) 详解相关推荐

  1. python描述器 触发事件_Python描述器引导(转)

    原文:http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html 作者: Raymond Hettinger 联系: 翻译: h ...

  2. python占位符补数据_Python 占位符格式化的简单示例

    这篇文章主要为大家详细介绍了Python 占位符格式化的简单示例,具有一定的参考价值,可以用来参考一下. 对python这个高级语言感兴趣的小伙伴,下面一起跟随512笔记的小编两巴掌来看看吧! 占位符 ...

  3. python 描述器 详解_Python描述器descriptor详解

    前面说了descriptor,这个东西其实和Java的setter,getter有点像.但这个descriptor和上文中我们开始提到的函数方法这些东西有什么关系呢? 所有的函数都可以是descrip ...

  4. python续行符是啥_python续行符

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! + 作为续行符时,在行尾使用了后,可以换行继续书写内容+ n 代表一个换行符+ ...

  5. python 多进程 调用模块内函数_python子进程模块subprocess详解与应用实例 之一

    分类: Python/Ruby 2014-09-09 10:59:42 subprocess--子进程管理器 一.subprocess 模块简介 subprocess最早是在2.4版本中引入的. su ...

  6. python average函数详解_python基础之函数详解

    Python基础之函数详解 一.函数的定义 到现在为止,我们已经掌握了Python的基本语法和数据类型等相关基础知识了,以进行一个项目的编写了,这个时候,就会发现,很多代码需要我们进行复制粘贴,这简直 ...

  7. python代码缩进和冒号_Python缩进和冒号详解

    对于Python而言代码缩进是一种语法,Python没有像其他语言一样采用{}或者begin...end分隔代码块,而是采用代码缩进和冒号来区分代码之间的层次. 缩进的空白数量是可变的,但是所有代码块 ...

  8. python定时任务每月1号_Python 定时任务框架 APScheduler 详解

    APScheduler 最近想写个任务调度程序,于是研究了下 Python 中的任务调度工具,比较有名的是:Celery,RQ,APScheduler. Celery:非常强大的分布式任务调度框架 R ...

  9. python读取数据的函数详解_python之文件读写详解

    打开文件 函数open() 参数说明: file:文件路径 mode: 文件的读写方式,默认'r',只读方式: buffering:设置缓冲策略,0用于二进制文件,1为行缓冲,用于文本模式:默认二进制 ...

最新文章

  1. 洛谷P2397 yyy loves Maths VI (mode) 摩尔投票
  2. 微信小程序直播如何接入?开源代码接入案例分享
  3. 解决weblogic页面和控制台乱码问题
  4. 解读思科2014-19年全球移动互联网发展趋势报告(1)
  5. php单例型(singleton pattern)
  6. android view添加背景,android – 如何将视图作为背景添加到surfaceView?
  7. 设置log缓存_全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析
  8. 使用python读写xlsx格式中的数据【xlrd、pywin32】
  9. MATLAB地图作为底图,Matlab中自带地图绘制WorldMap详解
  10. 矩形类的定义(java)
  11. vue init webpack缺少标识符_Vue脚手架热更新技术探秘
  12. 上万元游戏拼多多7块搞定 PICO防不住
  13. 百面机器学习03-经典算法
  14. thinkpad x250装黑苹果教程_Thinpad T系列安装MAC OS 黑苹果教程
  15. “挂羊头卖狗肉”的宇宙学谭
  16. 华北理工计算机学院官网,2019上半年华北理工大学计算机等级考试报名通知
  17. javaweb课程设计景点门票销售系统
  18. 两条命令让你的git轻松自动变基,学到了!
  19. 入门【必学】20个SEO优化术语
  20. CA-MKD:置信多教师知识蒸馏

热门文章

  1. Spring中父子容器的实现实例
  2. python中o_Python O
  3. (转)Delaunay三角剖分
  4. Horizon8基础环境准备08——CA证书
  5. capcreatecapturewindowa 说明_阳江陶瓷坯体增强剂使用说明
  6. error LNK2005: _DllMain@12 already defined in MSVC
  7. Linux下如何杀死终端
  8. 在另一台服务器上还原被误删的 MySQL 数据
  9. docker swarm
  10. python学习之老男孩python全栈第九期_数据库day004 -- 作业