Python属性、方法和类管理系列之----描述符类
什么是描述符类?
根据鸭子模型理论,只要具有__get__方法的类就是描述符类。
如果一个类中具有__get__和__set__两个方法,那么就是数据描述符,。
如果一个类中只有__get__方法,那么是非数据描述符。
__get__:当我们用类或者实例来调用该属性时,Python会返回__get__函数的结果。
__set__:当我们用实例来设置属性值时,Python会调用该函数。对类没有限制作用。
__delete__:当我们用实例试图删除该属性时,Python会调用该函数。对类没有限制作用。
非数据描述类
class Desc:
def __init__(self, value=22):
self.value= value
def __get__(self, ins, cls):
return self.value
class A:
v=Desc()
a=A()
上面的描述符类只有一个__get__属性,所以是非数据描述符。
>>> a.v #由于实例中没有v属性,所以找到了类的属性,而类的属性是一个描述符类实例,所以调用其__get__方法的结果。
22
>>> a.__dict__ #实例的__dict__空空如也。
{}
>>> A.__dict__ #类的__dict__中确实存在v属性,且是一个Desc object对象。
mappingproxy({'__module__': 'b', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, 'v': <b.Desc object at 0x7ff010f5f550>})
>>> a.v=30 #我们通过实例设置v属性,发现成功了。
>>> a.__dict__ #我们发现实例的__dict__中存入了我们刚才设置的属性
{'v': 30}
>>> A.__dict__ #类的__dict__没有发生任何变化
mappingproxy({'__module__': 'b', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, 'v': <b.Desc object at 0x7ff010f5f550>})
>>> a.v #如我们所料,访问到了a.__dict__中的内容。
30
>>> del a.v #我们删除实例的属性v后发现居然还是可以调用a.v,返回的是我们设置之前的值。
>>> a.v
22
>>> A.__dict__ #和前面一样,没有发生变化。
mappingproxy({'__module__': 'b', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, 'v': <b.Desc object at 0x7ff010f5f550>})
通过上面的测试,我们发现非数据描述类有如下特点:
- 如果实例
__dict__没有设置同名属性,那么返回描述类的__get__方法的结果。 - 如果实例
__dict__中存在同名属性,那么返回实例__dict__中的内容。 - 对我们设置实例的
__dict__中的行为并不做阻止。所以我说这是查看级别的描述类。
数据描述类
class Desc:
def __init__(self, value=22):
self.value= value
def __get__(self, ins, cls):
return self.value
def __set__(self, ins, value):
self.value=value
#raise AttributeError
class A:
v=Desc()
a=A()
运行结果如下:
>>> a.v
22
>>> a.v=10
>>> a.__dict__ #我们设置a.v后,发现实例的__dict__中仍然空空如也。因为此时调用的是__set__方法,值10存入到了Desc实例的value属性上了。
{}
>>> A.__dict__
mappingproxy({'__module__': 'b', '__doc__': None, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, 'v': <b.Desc object at 0x7f0d2a4de5c0>})
>>> a.v #此时得到的还是Desc的__get__方法返回的结果。
10
>>> del a.v #不允许我们删除
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
>>> A.v=30
>>> A.__dict__
mappingproxy({'__module__': 'b', '__doc__': None, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, 'v': 30})
#我们把__set__方法的原来语句注销,添加raise AttribeError语句,再次运行
>>> a.v=30 #我们在__set__中手动添加了AttributeError异常,所以我们再也不能设置a.v的值了,因此该属性鞭策了只读属性。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/aaa/proj/b.py", line 8, in __set__
raise AttributeError
AttributeError
>>> A.v=20 #通过类,仍然可以改变属性
>>> A.__dict__ #改变后,变成了普通属性20了,这时甚至都已经不再是描述符类了。
mappingproxy({'__doc__': None, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': 'b', '__weakref__': <attribute '__weakref__' of 'A' objects>, 'v': 20})
>>> del A.v
说明如下:
- 当
__set__方法存在后,实例设置同名属性时,完全需要看__set__的脸色。 - 如果描述类中
__set__方法存在但是__delete__方法不存在,那么不能删除客户类中的属性。 - 即使在
__set__方法中做了限制,这个限制只是对实例而言的,对类没有起到作用。
把属性存在描述符类中
class Desc:
def __init__(self, value):
self.value = value
def __get__(self, ins, cls):
return self.value
def __set__(self, ins, value):
self.value = value
def __delete__(self, ins):
raise AttributeError('not allowed to delete attribute name ' )
class A:
name=Desc('JS')
a=A()
执行结果如下:
>>> del a.name
>>> a=A()
>>> b=A()
>>> a.name
'JS'
>>> b.name
'JS'
>>> a.name='CC'
>>> b.name
'CC'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/aaa/proj/b.py", line 10, in __delete__
raise AttributeError('not allowed to delete attribute name ' )
AttributeError: not allowed to delete attribute name
缺点显而易见,如果有多个实例,那么他们共享一个描述符,所以当一个实例的该属性发生改变后,其他实例的该属性也会发生变化。
改善方法:
存入一个字典,把实例的hash作为健存入,这样可以解决问题。
class Desc:
def __init__(self, value):
self.values={}
def __get__(self, ins, cls):
return self.values[hash(ins)]
def __set__(self, ins, value):
self.values[hash(ins)]=value
def __delete__(self, ins):
raise AttributeError('not allowed to delete attribute name ' )
把数据存入实例中
class Desc:
def __get__(self, ins, cls):
return ins._name
def __set__(self, ins, value):
ins._name=value
def __delete__(self, ins):
raise AttributeError('not allowed to delete attribute name ' )
class A:
name=Desc()
a=A()
执行结果如下:
>>> a=A()
>>> a.name='JS'
>>> a.name
'JS'
>>> a._name='CC'
>>> a.name
'CC'
缺点:我们设置在实例中的变量私密性不太好,可以很容易被改变。
当然,可以做一个私有性的装饰器,或者利用属性扩张来解决,这是我在后面会介绍的内容。
补充解释
__get__(self, ins, cls):其中ins为实例对象,在我们上面的例子中是a或者b,cls为a或者b的类,为A
__set__和__delete__:ins和上面的含义相同
Python属性、方法和类管理系列之----描述符类的更多相关文章
- Python属性、方法和类管理系列之----__slots__属性
一句话说明 __slots__是用来限制实例的属性的,__slots__可以规定实例是否应该有__dict__属性:__slots__不能限制类的属性. 只有__slots__列表内的这些变量名可赋值 ...
- Python系列之 - 描述符
描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议 __get__():调用一个属性时,触 ...
- 11.python描述符---类的装饰器---@property
描述符1.描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()这三个内置方法中的一个,描述符也被称为描述符协议(1):__ ...
- Python属性、方法和类管理系列之----属性初探
在学习dict的时候,肯定听过dict是Python中最重要的数据类型,但是不一定知道为什么.马上你就会明白原因了. Python中从模块.到函数.到类.到元类,其实主要管理方法就是靠一个一个的字典. ...
- Python属性、方法和类管理系列之----元类
元类的介绍 请看位于下面网址的一篇文章,写的相当好. http://blog.jobbole.com/21351/ 实例补充 class Meta(type): def __new__(meta, c ...
- Python 属性方法、类方法、静态方法、 特殊属性__doc__ (内建属性)
总结:和类的关联性讲:属性方法>类方法>静态方法 属性方法@property:仅仅是调用方式不用+括号. 类方法@classmethod:访问不了累的属性变量,只可以访问类变量. 静态方法 ...
- python小知识-__call__和类装饰器的结合使用,数据描述符__get__\__set__\__delete__(描述符类是Python中一种用于储存类属性值的对象)
class Decorator(): def __init__(self, f): print('run in init......') self.f = f def __call__(self, a ...
- Python大法之告别脚本小子系列—信息资产收集类脚本编写(下)
作者:阿甫哥哥 原文来自:https://bbs.ichunqiu.com/article-1618-1.html 系列文章专辑:Python大法之告别脚本小子系列目录: 0×05 高精度字典生成脚本 ...
- USB学习小记-HID类键盘的报告描述符的理解
前言 断断续续的学习了将近三个月,才把USB的HID类搞明白,速度真是够慢的.利用晚上+周末的时间学习自己的东西确实是必要的,不过效率是有点低,以后要更专注一些才行,希望自己能做到吧. 在学习过程中, ...
随机推荐
- Python基础:1.数据类型(空、布尔类型、整型、长整型、浮点型、字符串)
提示:python版本2.7,windows系统 Python提供的基本数据类型:空.布尔类型.整型.长整型.浮点型.字符串.列表.元组.字典.日期 1.空(None) None,是一个特殊的值,不能 ...
- 어느 도시 보유 하 면 사랑 이다(事態が発生すれば、ある都市の恋はしません)【Si les villes un amour】{If have love in a city}
如果在北京拥有爱情 半夜在簋街喝啤酒 吃小龙虾 在后海的苦情歌声里 搂着你数那四合院的瓦片 如果在上海拥有爱情 去外滩手挽手 吹吹风 坐一下午 去城隍庙尝试各种小吃 嘲笑你嘴角残余的糯米糕 如果在杭州 ...
- 【原】NGUI中的UIRoot脚本功能
UIRoot是NGUI控件的根节点,使用是根据屏幕尺寸自动(或手动)调节节点下子控件的大小. 这个组件声明了在编辑模式下运行:[ExecuteInEditMode],在Inspector编辑修改属性值 ...
- java学习,从一个字符串中统计同一类型出现的次数
1.从字符串“AS345asdzf*())sddsWE”中统计大写字母.小写字母.其他类型的出现的次数 String s="AS345asdzf*())sddsWE"; int l ...
- SpringMVC使用@ResponseBody注解返回中文字符串乱码的问题
先说一下我的经历,以及解决问题的而过程. 在使用SpringMVC的时候,最开始的时候在配置文件中使用<mvc:annotation-driven />去自动注册DefaultAnnota ...
- ACM——五位以内的对称素数
http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1026 五位以内的对称素数 时间限制(普 ...
- solr-tomcat 中文乱码
<Connector URIEncoding="UTF-8" port="8080" protocol="HTTP/1.1" conn ...
- Strut2文件下载
Struts2控制文件下载,可以在文件下载之前做一些操作.这里就以权限控制为例,简单实现一下Struts2的文件下载. 一.Struts2文件下载的Action配置,是提供了一个能返回InputStr ...
- C# 多任务之 Task
Task 是什么 ? Task 是一个类, 它表示一个操作不返回一个值,通常以异步方式执行. Task 对象是一个的中心思想 基于任务的异步模式 首次引入.NET Framework 4 中. 继承层 ...
- php封装文件上传
这是一个经常在项目中遇到的问题,所以封装一个,分享给大家. 一,前期配置php.ini 如果上传文件超过了php配置那么$_POST或者$_FILES等都是空数组,这点是一个坑,因为那时候就不 ...