描述符(descriptor)
   descriptor 是一个实现了 __get__、 __set__ 和 __delete__ 特殊方法中的一个或多个的.

与 descriptor 有关的几个名词解释,
描述符类(descriptor class)
实现描述符协议的类,被称作'描述符类'. 托管类(managed class)
  把描述符实例声明为类属性的类,被称作'托管类'. 描述符实例(descriptor instance)
  描述符类的各个实例,声明为托管类的类属性. 托管实例(managed instance)
  托管类的实例。 储存属性(storage attribute)
  托管实例中存储自身托管属性的属性,被称作'储存属性'.这种属性与描述符属性不同,描述符属性都是类属性(self.X) 托管属性(managed attribute)
托管类中由描述符实例处理的公开属性,值存储在储存属性中。
也就是说,描述符实例和储存属性为托管属性建立了基础,这一点一定要理解. *** 描述符的用法是,创建一个实例,作为另一个类的类属性(其实例作为另一个类-'托管类'的类属性).
描述符管理'托管类'中'托管属性'的存取和删除,数据通常存储在'托管实例'中.
描述符协议涉及的特殊方法,
__get__(self, instance, owner),
获取 'owner' class 的'属性'(class attribute access) 或 '实例'(instance attribute access)
'owner' 始终是 'attribute' 或 'instance' 的 owner class(即托管类); 'instance' 的值为'托管实例',
或者为 None 在不是通过'托管实例'调用的情况下(托管类直接调用'类属性').
该方法返回描述符所管理相应 'attribute' 或丢出 'AttributeError exception'. __set__(self, instance, value),
设置托管类的 'instance' 的属性为 'value' __delete__(self, instance),
删除托管类的 'instance' 的属性 __set_name__(self, owner, name)
在托管类实例别创建的时候调用, 用来设置描述符的名字 'name' (__dict__ key) 注, 以上对特殊方法的描述中, 'attribute' 指的是托管类的 '__dict__' 的属性(name / key in __dict__ of owner class) 描述符分类,
Python 存取属性的方式是非'对称'的。通过实例读取属性时,通常返回的是实例中定义的属性;
但是,如果实例中没有指定的属性,那么会获取类属性。
而为实例中的属性赋值时,如指定属性不存在通常会在实例中创建属性,根本不影响类属性。
这种不'对称'的处理方式对描述符也有影响。根据是否定义 __set__ 方法,描述符可分为两大类。 覆盖型描述符,
实现 __set__ 方法的描述符属于覆盖型描述符,因为虽然描述符是类属性,但是实现 __set__ 方法的话,会覆盖对实例属性的赋值操作。
(特性 'property' 是覆盖型描述符:如果没提供 __set__ 方法, property 类中的 __set__ 方法会抛出 AttributeError 异常,指明那个属性是只读的。)
例子,
class Override(object): # 覆盖性描述符
def __set__(self, instance, value):
print('Override - SET - args :',(self,instance,value)) def __get__(self, instance, owner):
print('Override - GET - args :', (self, instance, owner)) class Override_Noget(object): # 没有实现 __get__ 方法的覆盖性描述符
def __set__(self, instance, value):
print('Override_Noget - SET - args :', (self, instance, value))
return class NonOverride(object): # 非覆盖性描述符
def __get__(self, instance, owner):
print('NonOverride - GET - args :', (self, instance, owner)) class Registor(object): # 覆盖与非覆盖描述符示例
Override = Override()
Override_Noget = Override_Noget()
#NonOverride = NonOverride() if __name__ == '__main__':
print('*'*8 + ' Override ' + '*'*8)
regist6 = Registor()
regist7 = Registor() print(vars(regist6))
regist6.Override
Registor.Override #
regist6.Override = 789 #
print(vars(regist6))
regist6.Override #
regist6.__dict__['Override'] = 911 #4 绕过描述赋, 向 __dict__ 字典赋值
print(vars(regist6)) #
print(regist6.Override)
print(regist6.__dict__['Override']) print('*' * 8 + ' Override_Noget ' + '*' * 8)
print(vars(regist7))
print(regist7.Override_Noget) #
print(Registor.Override_Noget) # regist7.Override_Noget = 'WOW' #
print(regist7.Override_Noget) regist7.__dict__['Override_Noget'] = 'sos' #7 通过实例的 __dict__ 属性设置名为 Override_Noget 的实例属性
print(vars(regist7))
print(regist7.Override_Noget) # regist7.Override_Noget = 'MOM'
print(regist7.Override_Noget) #
print(vars(regist7)) Output,
# ******** Override ********
# {}
# Override - GET - args : (<__main__.Override object at 0x03A83C10>, <__main__.Registor object at 0x03A83CB0>, <class '__main__.Registor'>)
# Override - GET - args : (<__main__.Override object at 0x03A83C10>, None, <class '__main__.Registor'>)
#1 通过 Registor 类直接调用 '类属性', 触发 __get__ 方法, 'instance' 参数是 'None'
# Override - SET - args : (<__main__.Override object at 0x03A83C10>, <__main__.Registor object at 0x03A83CB0>, 789)
#2 regist6.Override = 'female' 赋值, 触发 __set__ 方法, 参数 'value' 是 '789'
# {}
# Override - GET - args : (<__main__.Override object at 0x03A83C10>, <__main__.Registor object at 0x03A83CB0>, <class '__main__.Registor'>)
#3 regist6.Override 取值,触发 __get__ 方法
# {'Override': 911}
#4 vars(regist6)
# Override - GET - args : (<__main__.Override object at 0x03A83C10>, <__main__.Registor object at 0x03A83CB0>, <class '__main__.Registor'>)
# None
# # ******** Override_Noget ********
# {}
# <__main__.Override_Noget object at 0x01B43C90>
# <__main__.Override_Noget object at 0x01B43C90>
#5 由于没有实现 __get__ 方法,通过实例获取描述符会返回描述符本身
# Override_Noget - SET - args : (<__main__.Override_Noget object at 0x01B43C90>, <__main__.Registor object at 0x01B43D10>, 'WOW')
#6 regist7.Override_Noget 赋值,触发 __set__ 方法
# <__main__.Override_Noget object at 0x01B43C90>
# {'Override_Noget': 'sos'}
# sos
#7 Override_Noget 实例属性会'遮盖'描述符,但是只有读操作是如此(没有 __get__ 方法)
# Override_Noget - SET - args : (<__main__.Override_Noget object at 0x01B43C90>, <__main__.Registor object at 0x01B43D10>, 'MOM')
# {'Override_Noget': 'sos'}
# sos
#8 读取时,只要有同名的实例属性,描述符就会被遮盖 非覆盖型描述符
没有实现 __set__ 方法的描述符是非覆盖型描述符。如果设置了同名的实例属性,描述符会被遮盖,致使描述符无法处理那个实例的那个属性。
(方法是以非覆盖型描述符实现的)
例子,
class NonOverride(object): # 非覆盖性描述符
def __get__(self, instance, owner):
print('NonOverride - GET - args :', (self, instance, owner)) class Registor(object):
NonOverride = NonOverride() if __name__ == '__main__':
regist8 = Registor()
print('*' * 8 + ' PersonalInfo_NonOverride ' + '*' * 8)
regist8.NonOverride # print(vars(regist8))
regist8.NonOverride = '' #2 NonOverride 是非覆盖型描述符,因此实现 __set__ 方法
print(vars(regist8)) #
print(regist8.NonOverride) # del regist8.NonOverride
Registor.NonOverride #
print(vars(regist8))
print(regist8.NonOverride) Output,
# NonOverride - GET - args : (<__main__.NonOverride object at 0x01143D10>, <__main__.Registor object at 0x01143D90>, <class '__main__.Registor'>)
#1 regist8.NonOverride 触发 __get__ 方法
# {}
# {'NonOverride': '333'}
#
#2 现有名为 NonOverride 的实例属性,把 Registor 类的同名描述符属性遮盖掉
# NonOverride - GET - args : (<__main__.NonOverride object at 0x01143D10>, None, <class '__main__.Registor'>)
#3 描述符依然存在
# {}
# NonOverride - GET - args : (<__main__.NonOverride object at 0x01143D10>, <__main__.Registor object at 0x01143D90>, <class '__main__.Registor'>)
# None # *** *** 注, 描述符的 __set__ 方法实现了对'同名实例属性' '写' 的 '遮盖'; __get__ 方法 实现了对'同名实例属性' '读' 的 '遮盖' 描述符例子(描述符 相关逻辑比较复杂, 请耐心阅读示例),
例子背景是,一款有关锻炼的 APP 中有这样一个类,它用来记录个人锻炼数据,输入每天的锻炼数据后,给处针对性的锻炼建议.
这个类被创建的时候需要用户注册基本的个人信息(身高,年龄,性别). 考虑到实际应用, 参数 '身高','年龄'需要是非负数,
'性别'是 'Male' 或者 'Female', 其他的值对这个3个参数来说都是非法值. class PersonalInfo_Override(object): def __init__(self,elment):
self.elment = elment def __set__(self, instance, value):
print('SET - args :',(self,instance,value))
if isinstance(value,int):
if value > 0:
instance.__dict__[self.elment] = value # 直接从instance.__dict__ 中处理,是为了跳过'特性',防止无限递归
# setattr(instance,self.elment,value) # 会引起无限递归
else:
raise ValueError('Invalid value setting - \'age\' and \'height\' must INT type with the value > 0')
elif isinstance(value,str):
if value.lower() == 'male' or value.upper() == 'FEMALE':
instance.__dict__[self.elment] = value
else:
raise ValueError('Invalid value setting - \'SEX\' must STR type - \'male\' or \'FEMALE\'')
else:
raise ValueError('Invalid value setting - unsupported type - \'INT\' or \'STR\' type is prefer') def __get__(self, instance, owner):
print('GET - args :', (self, instance, owner))
if instance is None:
return self
else:
return instance.__dict__[self.elment] class PersonalInfo_Override_Noget(object): def __init__(self, elment):
self.elment = elment def __set__(self, instance, value):
if isinstance(value,int):
if value > 0:
instance.__dict__[self.elment] = value
else:
raise ValueError('Invalid value setting - \'age\' and \'height\' must INT type with the value > 0')
elif isinstance(value,str):
if value.lower() == 'male' or value.upper() == 'FEMALE':
instance.__dict__[self.elment] = value
else:
raise ValueError('Invalid value setting - \'SEX\' must STR type - \'male\' or \'FEMALE\'')
else:
raise ValueError('Invalid value setting - unsupported type - \'INT\' or \'STR\' type is prefer') class PersonalInfo_NonOverride(object): def __init__(self, elment):
self.elment = elment def __get__(self, instance, owner):
if instance is None:
return self
else:
return instance.__dict__[self.elment] class ExerciseRecord(object):
sex = PersonalInfo_Override('sex')
age = PersonalInfo_Override_Noget('age')
height = PersonalInfo_NonOverride('height') def __init__(self,sex,age,height):
self.sex = sex
self.age = age
self.height = height def suggestion(self):
pass Output,
if __name__ == '__main__':
regist1 = ExerciseRecord('male',25,180)
regist2 = ExerciseRecord('lala', 25, 180) # ValueError: Invalid value setting - 'SEX' must STR type - 'male' or 'FEMALE'
regist3 = ExerciseRecord('FEMALE', 0, 10) # ValueError: Invalid value setting - 'age' and 'height' must INT type with the value > 0
regist4 = ExerciseRecord('MALE', 2, -10) # 'height' 属性应用的是'非覆盖描述符'(没有实现 __set__ 方法), 由于数据的有效性的验证是在 __set__ 方法中实现的,
# 所以对于 'height' 属性来说并没有起到数据验证的作用
regist5 = ExerciseRecord('male',-2,-5) # ValueError: Invalid value setting - 'age' and 'height' must INT type with the value > 0 ExerciseRecord.sex # GET - args : (<__main__.PersonalInfo_Override object at 0x00FA3B50>, None, <class '__main__.ExerciseRecord'>)
# 通过类直接调用 '类属性', 触发 __get__ 方法, 'instance' 参数是 'None' object.__get__(self, instance, owner)
Called to get the attribute of the 'owner' class (class attribute access) or of an instance of that class (instance attribute access).
'owner' is always the owner class, while 'instance' is the instance that the attribute was accessed through,
or 'None' when the attribute is accessed through the owner.
This method should return the (computed) attribute value or raise an AttributeError exception. object.__set__(self, instance, value)
Called to set the attribute on an instance 'instance' of the owner class to a new value, 'value'. object.__delete__(self, instance)
Called to delete the attribute on an instance 'instance' of the owner class. object.__set_name__(self, owner, name)
Called at the time the owning class owner is created. The descriptor has been assigned to name. *** Note,
In the examples below, “the attribute” refers to the attribute whose name is the key of the property in the owner class’ __dict__.

Descriptor - Python 描述符协议的更多相关文章

  1. 杂项之python描述符协议

    杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用 ...

  2. Iterator Protocol - Python 描述符协议

    Iterator Protocol - Python 描述符协议 先看几个有关概念, iterator 迭代器, 一个实现了无参数的 __next__ 方法, 并返回 '序列'中下一个元素,在没有更多 ...

  3. 【转载】Python 描述符简介

    来源:Alex Starostin 链接:www.ibm.com/developerworks/cn/opensource/os-pythondescriptors/ 关于Python@修饰符的文章可 ...

  4. python描述符descriptor(一)

    Python 描述符是一种创建托管属性的方法.每当一个属性被查询时,一个动作就会发生.这个动作默认是get,set或者delete.不过,有时候某个应用可能会有 更多的需求,需要你设计一些更复杂的动作 ...

  5. Python 描述符(Descriptor) 附实例

    在 Python 众多原生特性中,描述符可能是最少被自定义的特性之一,但它在底层实现的方法和属性却无时不刻被使用着,它优雅的实现方式体现出 Python 简洁之美. 定义 一个描述符是一个有" ...

  6. python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解

     1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...

  7. python描述符 descriptor

    descriptor 在python中,如果一个新式类定义了__get__, __set__, __delete__方法中的一个或者多个,那么称之为descriptor.descriptor通常用来改 ...

  8. Python描述符 (descriptor) 详解

    1.什么是描述符? python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问.这些方法有 __get__(), __set__(), 和__delete__().如 ...

  9. 一文掌握 Python 的描述符协议

    描述符介绍 描述符本质就是一个新式类,在这个新式类中,至少要实现了__get__(),__set__(),__delete__()中的一个.这也被称为描述符协议. class Myclass(obje ...

随机推荐

  1. InteliJ IDEA 启动应用出现 Error:failed to create a child event loop 解决方案

    1.首先检查自己电脑是否能够联网 2.打开电脑防火墙允许 InteliJ IDEA 进行通信如下图所示

  2. Scala实践8

    1.1继承类 使用extends关键字,在定义中给出子类需要而超类没有的字段和方法,或者重写超类的方法. class Person { var name = "zhangsan" ...

  3. C++中全排列函数next_permutation 用法

    今天蓝桥杯刷题时发现一道字符串排序问题,突然想起next_permutation()函数和prev_permutation()函数. 就想写下next_permutation()的用法 next_pe ...

  4. Js 数组按数量分部!

    使用 reduce 将数组分为几个部分,每个部分最多10个! 相比其他语言使用 js  实现这个逻辑非常的简单方便! var group = function (source, step) { if ...

  5. windows服务搭建(VS2019创建Windows服务不显示安装组件)

    1.创建windows服务应用 2.右键查看代码 3.写个计时器Timer  using System.Timers; 如上图,按tab键快速操作  会自动创建一个委托 改为下边的方式,打印日志来记录 ...

  6. 曹工说Spring Boot源码(12)-- Spring解析xml文件,到底从中得到了什么(context:component-scan完整解析)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  7. C语言实现按位拷贝

    在程序开发过程中,我们经常会用到按位拷贝一串二进制数,在此提供一个按位拷贝的函数给大家参考,函数源码如下: #define char_t char #define uint8_t unsigned c ...

  8. Dappy如何防止DNS黑客入侵

    作者:Raphaël 译者注:Dappy是RChain生态中的DNS[域名系统(服务)协议].Dappy基于RChain的技术架构保障了域名系统的安全性. Dappy是一个用于文件和Web应用程序的去 ...

  9. 迈向Angular 2

    目录 序言 XV第1章 Angular 2快速上手 1Web的进化——新框架时代 2ECMAScript的进化 2Web Component 3WebWorker 4从AngularJS 1.x中学到 ...

  10. 通过识别页面内容获得返回值判断后续执行(exists指令的用法)

    本案例主要用到airtest 的exists指令 从指令解释可以知道,当判断某图片不存在的时候,会返回false值 脚本思路即为如果返回值==false则执行A,!=fales则执行B 下图脚本思路, ...