描述器
描述器必须是类属性,Python中,一个类实现了__get__ 、__set__、__delete__三个任意一个方法都称为描述 器;
如果一个类的类属性设置为描述器,那么它被称为此描述器的owner属主;
2.描述器的定义划分如果一个类仅仅实现了__get__()方法,称为非数据描述器non-data descriptor;
如果一个类实现了__get__(),__set__()方法,称为数据描述器data descriptor;
3.非数据描述器# 示例1classStudent1:def __init__(self):self.course =Pythonprint(Student1.__init__)classStudent2: stu1 =Student1()# Student1()返回的是Student1类的实例def __init__(self):print(Student2.__init__)print(Student2.stu1.course)# 创建Student2的实例对象stu2 =Student2()print(stu2.stu1.course)# 示例2, 引入描述器classStduent1:def __init__(self):self.course =Pythonprint(Stduent1.__init__)def __get__(self, instance, owner):print(self={} instance={} owner={}.format(self, instance, owner))classStduent2: stu1 =Stduent1()def __init__(self):print(Stduent2.__init__)print(Stduent2.stu1.course)# Stduent2.stu1会访问Stduent1的实例,默认会调用__get__方法,但是__get__方法没有将实例返回,因此,Stduent2.stu1.course会报错stu2 =Stduent2()print(stu2.stu1.course)# 一样的报错# 示例3, 引入描述器classStduent1:def __init__(self):self.course =Pythonprint(Stduent1.__init__)def __get__(self, instance, owner):# 这里的self为Stduent1的实例. instance为实例, 如果是类访问,那么instance为None. owner是调用者的类print(self={} instance={} owner={}.format(self, instance, owner))returnself# 返回Student1的实例selfclassStduent2: stu1 =Stduent1()def __init__(self):print(Stduent2.__init__)print(Stduent2.stu1.course)stu2 =Stduent2()print(stu2.stu1.course) 函数包含一个 __get__()方法以便在属性访问时绑定方法,这就是说所有的函数都是非数据描述器,它们返回绑定还是非绑定的方法取决于他们是被实例调用还是被类调用; 4.数据描述器# 示例1:classStudent1:def __init__(self):self.course =Pythonprint(Student1.__init__)def __get__(self, instance, owner):# 这里的self为Student1的实例. instance为实例, 如果是类访问,那么instance为None. owner是调用者的类print(self={} instance={} owner={}.format(self, instance, owner))returnself# 返回Student1的实例selfclassStudent2: stu1 =Student1()def __init__(self):print(Student2.__init__)self.y =Student1()# 没有调用__get__方法print(Student2.stu1.course)stu2 =Student2()print(stu2.y)# 示例2:数据描述器classStudent1:def __init__(self):self.course =Pythonprint(Student1.__init__)def __get__(self, instance, owner):print(self={} instance={} owner={}.format(self, instance, owner))returnselfdef __set__(self, instance, value):print(self={} instance={} value={}.format(self, instance, value))self.course = valueclassStudent2: stu1 =Student1()def __init__(self):print(Student2.__init__)self.y =Student1()# 调用了__get__方法print(Student2.stu1.course)stu2 =Student2()print(stu2.stu1)当非数据描述器是实例的变量时,实例访问非数据描述器不会调用__get__方法,只是访问了描述器类的实例;
当数据描述器是实例的变量时,实例访问数据描述器会调用描述器的__get__方法;
5.非数据描述器和数据描述器的访问顺序 当存在描述器的时候,一个类实例的查找属性顺序为:先查找类或父类中是否有数据描述器属性,如果有那么,先访问数据描述器,如果没有数据描述器 —> 那么就会查找自己实例的dict属性,如果dict属性里面也没有找到 —> 然后会在类或父类的非数据描述器进行查找; 6.非数据描述器和数据描述器的访问顺序的本质# 示例1:非数据描述器classStudent1:def __init__(self):self.course =Pythonprint(Student1.__init__)def __get__(self, instance, owner):print(self={} instance={} owner={}.format(self, instance, owner))returnselfclassStudent2: stu1 =Student1()def __init__(self):print(Student2.__init__)self.x =Student1print(Student2.stu1.course)stu2 =Student2()print(stu2.stu1)print(stu2.__dict__)# 实例的__dict__属性中有 {stu1: Student1}# 示例2:数据描述器classStudent1:def __init__(self):self.course =Pythonprint(XKD1.__init__)def __get__(self, instance, owner):print(self={} instance={} owner={}.format(self, instance, owner))returnselfdef __set__(self, instance, value):print(self={} instance={} value={}.format(self, instance, value))self.course = valueclassStudent2: stu1 =Student1()def __init__(self):print(Student2.__init__)self.x =Student1print(Student2.stu1.course)stu2 =Student2()print(stu2.stu1)print(stu2.__dict__)# 实例的__dict__为空事实上,实例属性的查找顺序并没有改变,依然是实例的dict中的属性优先被访问;
只是如果实例有属性是数据描述器的话,属性会被dict字典移除,因此就会访问类的属性,造成了数据描 述器优先访问的假象;
7.哪些案例是描述器实现的属性装饰器@property(),是通过数据描述器实现;
类方法装饰器@classmethod和静态方法装饰器@staticmethod都是通过非数据描述器实现;
8.描述器演练(1)@staticmethod的实现
classStaticMethod:def __init__(self, fn):self.fn = fndef __get__(self, instance, owner):print(self={} instance={} owner={}.format(self, instance, owner))returnself.fnclassStduent:@StaticMethoddef show():# show = StaticMethod(show) , 此时的show方法,已经是StaticMethod类的实例print(静态方法实现)Stduent.show()Stduent().show() 静态方法装饰器@staticmethod装饰的方法被装饰成了StaticMethod类的实例,可以看做一个类变量,因此类和 类的实例都可以访问这个类变量,那么就会调用__get__方法,返回self.fn,也就是原生的方法对象,最后调用这个方法对象;(2)@ClassMethod的实现
from functools importpartialclassClassMethod:def __init__(self, fn):self.fn = fndef __get__(self, instance, owner):print(self={} instance={} owner={}.format(self, instance, owner))returnpartial(self.fn, owner)classStduent:@ClassMethoddef show(cls):# show = ClassMethod(show)print(类方法实现)Stduent.show()Stduent().show() 类方法的实现与静态方法类似,只是需要调用partial偏函数,将类提前作为参数传递; 9.属性装饰器property实现原理分析classProperty:def __init__(self, fget, fset=None):self.fget = fgetself.fset = fsetdef __get__(self, instance, owner):if instance isnotNone:returnself.fget(instance)returnselfdef __set__(self, instance, value):if callable(self.fset):self.fset(instance, value)else:raiseAttributeError({} is not callable.format(self.fset.__name__))def setter(self, fn):self.fset = fnreturnselfclassStduent:def __init__(self, data):self.__data = data@Property# data = Property(data)def data(self):returnself.__data@data.setter # data = data.setter(data)def data(self, value):self.__data = valuestu=Stduent(Python)print(stu.data)stu.data =JAVAprint(stu.data)Python内置的@property装饰器,把一个方法变成可以像属性那样,做取值用;
@data.setter装饰器,把一个方法变成可以像属性那样,作赋值用;
@property和data.setter()同时使用,表示可读可写;
扫一扫,关注我们