在交互式环境中输入:
1 >>> class A: 2 a=0 3 def __init__(self): 4 self.a=10 5 self.b=100 6 7 8 >>> a=A() 9 >>> a.a10 1011 >>> a.b12 10013 >>> A.a14 015 >>> A.b16 Traceback (most recent call last):17 File "<pyshell>", line 1, in <module>18 A.b19 AttributeError: type object 'A' has no attribute 'b'20 >>></module></pyshell>
如下图:
还是在交互式环境中:
1 >>> class A: 2 a=0 3 def __init__(self): 4 self.a=10 5 self.b=100 6 7 8 >>> a=A() 9 >>> getattr(a,'a')#用getattr()函数获取实例a中a属性的值10 1011 >>> setattr(a,'a',20)#设置实例a中a属性的值为2012 >>> getattr(a,'a')#用getattr()函数获取实例a中a属性的值13 2014 >>> hasattr(a,'b')#测试实例a中是否包含属性b15 True
图片展示:
这种反射机制的用字符串来操作类的属性和方法的三个函数并不常用。编写框架等特殊项目是采用到。
1 class Washer: 2 3 def __init__(self,water=10,scour=2): 4 self._water=water #不想让用户直接访问实例变量,可以标志成私有 5 self.scour=scour 6 #属性包装,将water属性包装成方法,用户使用water时实际是访问的方法 7 @property 8 def water(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性 9 return self._water10 11 def set_water(self,water):12 self.water=water 13 14 def set_scour(self,scour):15 self.scour=scour 16 17 def add_water(self):18 print('Add water:',self.water)19 20 def add_scour(self):21 print('Add scour:',self.scour)22 23 def start_wash(self):24 self.add_water()25 self.add_scour()26 print('Start wash...')27 28 if __name__=='__main__':29 w=Washer()30 #w.start_wash()31 print(w.water)# 可以像访问属性一样访问方法
但这时用户仍然可以通过w._water来访问实例属性,封装的不好,也不会自动检查数据是不是浮点型,不好。
怎么解决?
用@属性.setter
1 class Washer: 2 3 def __init__(self,water=10,scour=2): 4 self._water=water #不想让用户直接访问实例变量,可以标志成私有 5 self.scour=scour 6 #属性包装,将water属性包装成方法,用户使用water时实际是访问的方法 7 @property 8 def water(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性 9 return self._water10 11 @water.setter #新添加代码12 def water(self,water):13 if 0<water></water>
结果:
另外,很好奇@water.setter这个water到底是个什么东西,发现可以理解成一个新的实例属性,跟构造函数的形参没有关系。比如下图
再比如:
结果仍为:
同样可以包装一个删除变量,@water.delete
-------------------------------------
这块代码表示water这个变量可以重写,
------------------------------------------
上面这块代码表示water属性可以读取。
---------------------------------------------
最后一个用法是用属性装饰器@property来新定义一个虚拟的属性。
1 class Washer: 2 3 def __init__(self,water=10,scour=2): 4 self._water=water #不想让用户直接访问实例变量,可以标志成私有 5 self.scour=scour 6 self.year=2000#这是生产日期 7 #属性包装,将water属性包装成方法,用户使用water时实际是访问的方法 8 @property 9 def water1(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性10 return self._water11 12 @water1.setter13 def water1(self,water):14 if 0<water></water>
运行结果:
描述符
描述符的意义是避免重复写具有相同限定属性的实例属性的定义代码。比如下面的例子:
1 class NonNeg:#数据描述符 2 def __init__(self,default=0):#构造方法 3 self.default=default#一个实例属性 4 def __get__(self,instance,owner):#协议方法 5 return self.default 6 def __set__(self,instance,val):#协议方法 7 if val>0: 8 self.default=val 9 else:10 print('The value must be NonNegative!')11 def __delete__(self,instance):#协议方法12 pass13 class Movie:14 rating=NonNeg()#描述符类NonNeg作另一个类Movie的属性,rating是Movie的类属性。15 score=NonNeg()16 17 if __name__=='__main__':18 m=Movie()19 print('rating:',m.rating)20 print('score:',m.score)#输出默认值default21 m.rating=80#使用__set__协议方法22 print('rating:',m.rating)#使用到 __get__协议方法23 m.score=-324 print('score:',m.score)
输出结果:
---------------------------------------
下面说明所有的 类成员函数都是非数据描述符。
在这个交互式环境中可以看出pr这个类方法仅有__get__协议,三个不全,所以是非数据描述符。
----------------------------------------------------------------
同名的实例属性和非数据描述符(以类方法为例)同时出现时,访问的优先级是什么?
再看:
为啥结果还不一样了?再做一遍老师的例子:
重新打开idel之后重新写了一遍:
总结如下:
在交互式环境中,
若在类内实例方法中定义与此方法名想同的实例变量pr,则在类外实例化此类后,实例.pr 首先访问的是此实例变量,实例.pr() 肯定访问的是类内实例方法。若再类外实例中定义一个 实例.pr=20,则再访问 实例.pr时则访问的是刚定义的实例属性 实例.pr=20。
若在类内没有定义与类方法同名的实例属性,则实例.pr访问的是类内的实例方法,若又在类实例化后实例下定义同名的的实例属性pr,则 实例.pr访问的刚定义的。。。
感觉好混乱:若访问过t.pr()再访问t.pr,t.pr就为10了,若没有访问过t.pr()直接访问t.pr,这个就先访问的是method Tst.pr of <__main__.tst object>
1 class Tst: 2 def pr(self): 3 self.pr=10 4 print('Tst') 5 t1=Tst() 6 t1.pr()#输出Tst 7 t1.pr#啥都没有输出 8 print(t1.pr)#输出10 9 print('下面实例化后不访问t.pr()直接访问t.pr:')10 t2=Tst()11 t2.pr#啥都没输出12 print(t2.pr)#输出了bound method Tst.pr of <__main__.tst></__main__.tst>
但后来在实例下新定义的同名实例属性会覆盖原先类中定义的实例方法。优先级知道了吧。
扩展:
1 class Tst: 2 def __init__(self,default=1): 3 self.water=default 4 def __call__(self): 5 print('包含call函数的类,他的实例可以直接当做函数使用。') 6 def info(self): 7 print("pass") 8 9 t=Tst()10 t()
当调用t()时只调用类中__call__函数。
--------------------------------------------
解答如下:
1 class surfaceNum:#定义一个描述类 2 def __init__(self,default=1): 3 self.number=default 4 def __get__(self,instance,owner):#参数instance和owner暂时没有用到,只有self是固定名参数 5 return self.number 6 def __set__(self,instance,val):#参数instance暂时没有用到 7 if 0<val></val>
这个题目是上节课题目的拔高,上节课题目及解答见链接:
以上就是深入类的属性介绍与使用的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2024 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号