python描述符
描述符定义
描述符是一种类,我们把实现了__get__()、__set__()和__delete__()中的其中任意一种方法的类称之为描述符。
描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在被使用类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例,我们可以通过查看实例和类的字典来确认这一点。
描述符是实现大部分Python类特性中最底层的数据结构的实现手段,我们常使用的@classmethod、@staticmethd、@property、甚至是__slots__等属性都是通过描述符来实现的。它是很多高级库和框架的重要工具之一,是使用到装饰器或者元类的大型框架中的一个非常重要组件。注:装饰器和元类等概念我们在以后文章中说明。
如下示例一个描述符及引用描述符类的代码:
class Descriptors: def __init__(self, key, value_type): self.key = key self.value_type = value_type def __get__(self, instance, owner): print("执行Descriptors的get") return instance.__dict__[self.key] def __set__(self, instance, value): print("执行Descriptors的set") if not isinstance(value, self.value_type): raise TypeError("参数%s必须为%s"%(self.key, self.value_type)) instance.__dict__[self.key] = value def __delete__(self, instance): print("执行Descriptors的delete") instance.__dict__.pop(self.key) class Person: name = Descriptors("name", str) age = Descriptors("age", int) def __init__(self, name, age): self.name = name self.age = age person = Person("xiaoming", 15) print(person.__dict__) person.name person.name = "jone" print(person.__dict__)
其中,Descriptors类就是一个描述符,Person是使用描述符的类。
类的__dict__属性是类的一个内置属性,类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里。
在输出描述符的变量时,会调用描述符中的__get__方法,在设置描述符变量时,会调用描述符中的__set__方法。
如上例子的运行结果如下:
描述符的种类和优先级
描述符分为数据描述符和非数据描述符。
至少实现了内置__set__()和__get__()方法的描述符称为数据描述符;实现了除__set__()以外的方法的描述符称为非数据描述符。
描述符的优先级的高低顺序:类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()。
在上述“描述符定义”章节的例子中,实例person的属性优先级低于数据描述符Descriptors,所以在赋值或获取值过程中,均调用了描述符的方法。
如下例子描述了类属性高于数据描述符:
class Descriptors: def __get__(self, instance, owner): print("执行Descriptors的get") def __set__(self, instance, value): print("执行Descriptors的set") def __delete__(self, instance): print("执行Descriptors的delete") class University: name = Descriptors() def __init__(self, name): self.name = name University.name University.name = "深圳大学" print(University.name) my_university = University("厦门大学") my_university.name print(my_university.name)
在该例子中,University类的变量被赋值,由于该优先级最高,所以使用的一直为University类的变量,示例运行结果如下:
如果在这个例子中,删除University.name = "深圳大学"这一行,则打印是怎么样的?
为什么对象的name属性没有值了呢,因为对象的的优先级没有描述符高,所以使用描述符的值,而描述符没有赋值,所以为None,所以虽然对象的属性有值,仍然无法正常获取。
描述符相关的优先顺序,理解难度稍微大一点,请各位看官仔细品味,大家可以自己写小例子尝试打印结果,可以让您更快的理解该原理。
参考:https://blog.csdn.net/chenzhanhai/article/details/84350403