zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    理解MVC MVVM MVP设计模式
    跨域问题详解
    JWT认证
    Django缓存
    API文档自动生成
    DRF分页器
    DRF请求和响应,以及Response对象重新封装
    drf认证、权限、频率、过滤、排序、异常处理
    drf之视图组件以及自动化路由
    【python面向对象实战练习】植物大战僵尸
  • 原文地址:https://www.cnblogs.com/zhubinglong/p/6947300.html
Copyright © 2011-2022 走看看