46讲描述符章节和property()的理解
在第46讲,小甲鱼讲了有关描述符的知识,看完十几分钟的视频有点懵逼。下来自己又仔细思考了一下,记了一些笔记。主要还是对小甲鱼的两个例子,加上自己个人的理解解读。欢迎大家批评指正,共同学习。描述符
描述符就是 将某种特殊类型的类的实例指派给另一个类的属性。
有关的魔法方法有以下三种:
__get__(self,instance,owner):
用于访问属性,它返回属性的值。
__set__(self,instance,value):
将在属性分配操作中调用,不返回任何内容。
__delete__(self,instance):
控制删除操作,不返回任何内容。
所谓特殊类型的类,就是含有以上三种魔法方法的类。然后把这个特殊的类的实例,指派给另一个类的属性(注意是类属性!!!而非实例属性。见后文。)。例如如下代码示例:
=======================================================================
class mydescriptor:
def __get__(self,instance,owner):
print('gettttttt',self,instance,owner)
def __set__(self,instance,value):
print('settttttt',self,instance,value)
def __delete__(self,instance):
print('delllllll',self,instance)
class Test:
x = mydescriptor() # x为Test的类属性
=======================================================================
定义了一个mydescriptor的类,重写了 __get____set____delete__ 魔法方法。然后又定义了一个Test的类,将mydescriptor的类实例化,然后指派给Test的属性x。
运行后结果如图1:
输入Test的实例化加上·之后,会自动弹出来相应的属性x,如图2。接下来对实例化对象,通过x进行操作,结果如图3:
可以看出 __set__(self,instance,value)的三个参数分别为:
self 指的就是tst.x这个经过mydescriptor()实例化后的对象(而它本身又是Test类的一个属性,实例化后的Test类对象也可以访问这个属性);instance 指的就是tst这个Test类实例化后的对象;value则是要赋给x这个属性的值,也就是30.
__get__(self,instance,owner) 中的owner指的是,Test 这个类对象本身。
而其中的self,instance以及 __delete__中的相应参数意义同上。
但是进行del tst.x操作后,访问tst.x依然可以,没有报错。因为这里只是通过tst.x执行了mydescriptor中的__delete__ 操作,而不是执行删除类属性的操作,所以tst.x还可以访问,没有问题。
下一个例子——实现自己的property()函数:
=======================================================================
class myypro:
def __init__(self,fget = None,fset = None,fdel = None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self,instance,owner):
print('动静のget')
return self.fget(instance)
def __set__(self,instance,value):
print('有点动静のset')
self.fset(instance,value)
def __delete__(self,instance):
print('一不小心9删掉了')
self.fdel(instance)
class C:
def __init__(self):
self.xx = None
def getx(self):
return self.xx
def setx(self,value):
self.xx = value
def delx(self):
del self.xx
x = myypro(getx,setx,delx)
=======================================================================
执行结果如图4:
实例化一个c1,然后通过c1.x对c1.xx进行赋值,执行myypro()中的 __set__方法。
然后输入: c1.x 和c1.xx 结果都为30. 利用del c1.x删除c1.xx,直接删除掉xx这个属性,若是再访问就会报错,提示C这个类没有xx这个属性。(这里c1.x就是类属性,而c1.xx则是实例属性)
整个过程理解起来就是:C是一个商人,getx、setx、delx是机器,而xx这个属性可以是材料。对这些材料进行加工,进行一系列操作,如果利用c1.get()等这些操作也可以,但是每次需要调用不同的方法(可以理解为需要不同的工人),比较麻烦,效率比较低。于是就从外面请了一个人,这个人能力比较强,什么都会,然后商人给了他一定的权利让他可以使用自己的机器来加工材料。这个人就是myypro!
譬如执行c1.x = 30 赋值操作的时候,C的实例化对象c1的属性c1.x是myypro的实例化对象,当对c1.x进行赋值的时候,会自动调用myypro中的 __set__ 方法,根据上面一个例子可以知道, __set__(self,instance,value)方法的参数self是c1的属性x;instance 是C的实例化对象c1,而value 是 赋值等式中右端的值,在这里也就是30。
又因为在myypro的 __init__方法中,定义了 self.fset = fset,而在C这个类对象中有 x = myypro(getx,setx,delx),因此self.fset也就等于传入的参数setx了,也就是说在myypro中,__set__ 方法中的 self.fset(instance,value) 也就等于C中的
setx(self,value)。并且两者对应完全一致。后者中的self也就是指c1,而前者中的instance也是c1,后面的value值也一样。因此就间接的通过x这个接口,实现了C中setx的功能,对c1.xx实现了赋值。后面的c1.x和 del c1.x的操作分析类似。
这样就可以实现小甲鱼在前面讲property()方法章节时所说的,只给用户一个端口,就可以进行赋值和删除等操作,而避免用户直接去执行getx、setx、delx这些函数,对用户来说是一个很好的体验。描述符以及property()函数的意义也就在于此!!!
类属性和实例属性
参照学习以下网站链接:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319117128404c7dd0cf0e3c4d88acc8fe4d2c163625000
https://www.cnblogs.com/wgDream/p/6749643.html
由于python是动态语言,根据类创建的实例可以绑定任意属性。
给实例绑定属性的方法是通过实例变量,或者通过self变量:
=======================================================================
class Student():
def __init__(self,name):
self.name = name
s = Student('xiaoyang')
s.score = 90
=======================================================================
这里通过 __init__ 方法定义的 self.name是实例化对象的属性,通过实例化对象s可以访问到;绑定属性也可以直接赋值的操作,例如 s.score = 90 来实现。
利用s.__dict__可以看到,如下图5:
此时,利用类对象Student来访问name等属性,就不行了。因为这些属性都是实例化对象的属性,而非类属性。如图6:
如果一个类本身需要一个属性,可以直接在class中定义属性,这种属性是类属性,归类所有,如下:
=======================================================================
class Student():
name = 'xiaoyang'
=======================================================================
这个name就是类属性,归类所有,但是所有的实例对象都可以访问到。
如果再给实例化对象绑定属性,那么实例属性将会改变,而不会改变类属性。
此时若删除实例属性的话,不会改变类属性,而是删掉了刚才赋值的新的实例属性,这时调用 s.name,由于实例的name属性没有找到,类的name属性就显示出来了。
若是删掉类属性的话,不会改变实例属性,如图9:
廖雪峰网站的总结:
1.实例属性属于各个实例所有,互不干扰;
2.类属性属于类所有,所有实例共享一个属性;
3.不要对类属性和实例属性使用相同的名字,否则将产生难以发现的错误。
因此一定要区分类属性和实例属性。
很详细,学习了 谢谢 谢谢,有一个问题不太明白。
__set__ 方法中的 self.fset(instance,value) 也就等于C中的
setx(self,value)。并且两者对应完全一致。
也就是说,在执行c1.x = 30时,自动调用myypro的__set__方法,也就是执行print('有点动静のset')和self.fset(instance,value)语句。
确实,执行self.fset(instance,value)就等于执行setx(self,value)。
我的疑问是,运行setx(self,value)语句是如何操作的?调用方法不是应该self.setx(value)吗?我表达的清楚吗? 本帖最后由 Tigeroad 于 2020-5-27 13:47 编辑
jiajiaself 发表于 2020-2-26 13:12
谢谢,有一个问题不太明白。
__set__ 方法中的 self.fset(instance,value) 也就等于C中的
setx(self,valu ...
在class C中已经定义了函数了:
def setx(self,value):
self.xx = value
到你说的那一步就是一个简单的调用函数的问题。 评论下,免得之后找不到O 有点小乱…… 写的超棒!!!!!! 强人♂锁男 发表于 2018-11-7 13:33
很详细,学习了
谢谢,能帮到你,很开心 hhheshenme 发表于 2020-8-4 18:47
写的超棒!!!!!!
谢谢你的夸奖,大家一起学习。 _2_ 发表于 2020-7-17 20:14
有点小乱……
自己的语言组织能力还有待提高。{:5_99:} 鼋头鱼 发表于 2020-7-17 20:11
评论下,免得之后找不到O
哈哈,谢谢支持{:5_109:}
页:
[1]