python面向对象-2深入类的属性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python面向对象-2深入类的属性相关的知识,希望对你有一定的参考价值。

技术分享

在交互式环境中输入:

 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.a
10 10
11 >>> a.b
12 100
13 >>> A.a
14 0
15 >>> A.b
16 Traceback (most recent call last):
17   File "<pyshell#10>", line 1, in <module>
18     A.b
19 AttributeError: type object A has no attribute b
20 >>> 

 如下图:

技术分享


技术分享

还是在交互式环境中:

 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 10
11 >>> setattr(a,a,20)#设置实例a中a属性的值为20
12 >>> getattr(a,a)#用getattr()函数获取实例a中a属性的值
13 20
14 >>> hasattr(a,b)#测试实例a中是否包含属性b
15 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._water
10 
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._water
10     
11     @water.setter   #新添加代码
12     def water(self,water):
13         if 0<water<=500:
14             self._water=water
15         else:
16             print(set Failure!)
17     
18     def set_water(self,water):
19         self.water=water        
20 
21     def set_scour(self,scour):
22         self.scour=scour        
23 
24     def add_water(self):
25         print(Add water:,self.water)
26 
27     def add_scour(self):
28         print(Add scour:,self.scour)
29 
30     def start_wash(self):
31         self.add_water()
32         self.add_scour()
33         print(Start wash...)
34         
35 if __name__==__main__:
36     w=Washer()
37     print(w.water)# 可以像访问属性一样访问方法
38     #w._water=20 #为了不让用户这样直接给实例属性赋值,用下面的语句
39     w.water=123 #可以像给属性赋值一样给方法赋值,并检测范围
40     print(w.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._water
11     
12     @water1.setter
13     def water1(self,water):
14         if 0<water<=500:
15             self._water=water
16         else:
17             print(set Failure!)
18     @property
19     def total_year(self): #定义一个虚拟实例属性,这个属性其实是一个方法,但是可以按照属性来用
20         return 2017-self.year
21     
22     def set_water(self,water):
23         self.water=water        
24 
25     def set_scour(self,scour):
26         self.scour=scour        
27 
28     def add_water(self):
29         print(Add water:,self.water)
30 
31     def add_scour(self):
32         print(Add scour:,self.scour)
33 
34     def start_wash(self):
35         self.add_water()
36         self.add_scour()
37         print(Start wash...)
38         
39 if __name__==__main__:
40     w=Washer()
41     print(w.water1)# 可以像访问属性一样访问方法
42     #w._water=20 #为了不让用户这样直接给实例属性赋值,用下面的语句
43     w.water1=123
44     print(w.water1)# 可以像访问属性一样访问方法
45     print(w.total_year)
46   
47     

 运行结果:

技术分享

 

 

 

 


描述符

技术分享

 描述符的意义是避免重复写具有相同限定属性的实例属性的定义代码。比如下面的例子:

 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         pass
13 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)#输出默认值default
21     m.rating=80#使用__set__协议方法
22     print(rating:,m.rating)#使用到 __get__协议方法
23     m.score=-3
24     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 object

但后来在实例下新定义的同名实例属性会覆盖原先类中定义的实例方法。优先级知道了吧。

 


 技术分享

 

技术分享

 扩展:

技术分享

 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<7 and isinstance(val,int)==True:
 8             self.number=val
 9             Box.info_num(self)#Box类还没有创建,故不能引用Box.infor_num,哈哈,能创建啊
10         else:
11             print(please set the correct surface number!)
12     def __delete__(self,instance):#协议方法
13          pass
14             
15 class Box:#定义一个类名为Box,类名后不必有括号,类包含类属性和类方法,这个类没有定义类属性
16     ‘‘‘这是一个计算体积的类‘‘‘#这是这个类的__doc__属性,执行类后就可以在交互界面输入Box.__doc__查看这行说明文字了
17     openstate=0
18     number=surfaceNum()
19     def __init__(self):#这是类的构造函数,当实例化Box后会自动调用这个__init__方法
20         self.length=0.0 #这是实例属性,在类内访问用self.length,在类外访问用  实例名.length
21         self.width=0.0
22         self.height=0.0
23         self._color=red       
24         self.__valum=0.0#双下换线开头的变量表示私有变量,所以他为私有实例属性,只能在类内访问到
25         
26     @property
27     def color(self):
28         return self._color
29     @color.setter
30     def color(self,color):
31         self._color=color
32         
33     def set_color(self,color):
34         self._color=color    
35         
36     def computevalum(self):#定义了一个类方法。
37         self.__valum=self.length*self.width*self.height
38         print(长度=,self.length,宽度=,self.width,高度=,self.height,valum=,self.__valum)
39 
40     def info_color(self):
41         #self.set_color(self._color)#在类中,函数调用函数的方式
42         print(Box的颜色为,self._color)
43 
44     def open_box(self):
45         if Box.openstate==0:
46             print(打开了Box)
47             Box.openstate=1
48         else:
49             print(Box已经打开了,不能重复打开)
50     def info_num(self):
51         #self.set_color(self._color)#在类中,函数调用函数的方式
52         
53         print(Box面上的数字为,Box.number)
54     #定义 __call__  函数,输出体积
55     def __call__(self):
56         self.__valum=self.length*self.width*self.height
57         print(长度=,self.length,宽度=,self.width,高度=,self.height,调用自身computa()输出:valum=,self.__valum)
58         
59 
60     
61     
62 if __name__==__main__:       
63     computa=Box() #实例化Box类
64     computa.number =2
65     computa.info_num()
66     computa.length=1
67     computa.width=2
68     computa.height=3
69     computa.computevalum()
70     computa()#实例名函数调用__call__函数直接输出体积
71     computa.set_color (yellow)
72     computa.info_color()
73     computa.open_box()
74     computa.color=green
75     computa.info_color()
76     print(‘‘)
77 
78     computb=Box()#实例化Box类
79     computb.length=2
80     computb.width=2
81     computb.height=3
82     computb.computevalum()
83     computb.set_color (black)
84     computb.info_color()
85     computb.open_box()
View Code

这个题目是上节课题目的拔高,上节课题目及解答见链接http://www.cnblogs.com/zhubinglong/p/6942289.html

 

以上是关于python面向对象-2深入类的属性的主要内容,如果未能解决你的问题,请参考以下文章

面向对象----属性,约束,super深入了解

python--面向对象-成员

python之路之前没搞明白4面向对象(封装)

python 面向对象专题:继承

python 面向对象专题:继承

Python面向对象编程02:深度认识类class