关于property的一些记录,以及描述符(descriptor)中__get__,__set__,__delete__的属性使用。

首先先介绍一下property的类,因为需要深入了解property就随便了解了下描述符,property实现就用了这个功能。

 

property以前在我脑子里面就是方法转属性的装饰圈,现在回想虽然也对,但只不过是里面功能的冰山一角。

 

首先介绍property的用法,后面上代码:

In [11]: class Rectange: 
    ...:     def __init__(self): 
    ...:         self.width = 0 
    ...:         self.height = 0 
    ...:  
    ...:     def set_size(self, size): 
    ...:         self.width, self.height = size 
    ...:  
    ...:     def get_size(self): 
    ...:         return self.width, self.height 
    ...:  
    ...:     def del_size(self): 
    ...:         del self.width,self.height 
    ...:  
    ...:     size = property(get_size, set_size, del_size) 
    ...:                                                                                                         

In [12]: r = Rectange()                                                                                          

In [13]: r.size                                                                                                  
Out[13]: (0, 0)

In [14]: r.size = 5,5                                                                                            

In [15]: r.size                                                                                                  
Out[15]: (5, 5)

In [16]: r.height                                                                                                
Out[16]: 5

In [17]: del r.size                                                                                              

In [18]: r.size                                                                                                  
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-18-f01c9e7af2ea> in <module>
----> 1 r.size

<ipython-input-11-6b0a6fffb0c4> in get_size(self)
      8 
      9     def get_size(self):
---> 10         return self.width, self.height
     11 
     12     def del_size(self):

AttributeError: 'Rectange' object has no attribute 'width'

In [19]: r.height                                                                                                
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-19-9fec4be02cd3> in <module>
----> 1 r.height

AttributeError: 'Rectange' object has no attribute 'height'

根据实际操作,size已变成了property的实例,调用size可以对多属性进行查询,复制,删除,使用起来非常的方便。

property后面后面共有四个关键字参数,fget,fset,fdel,doc,doc参数我没用过,官方解释是一个带文档字符串的特性。

所以在实例化的时候必须关键词参数的顺序输入读取属性,写入属性,删除操作的顺序

这是第二种property的使用,效果都一样,这个用的是装饰器的用法。(非常不好用)

In [36]: class Rectange: 
    ...:     def __init__(self): 
    ...:         self.width = 0 
    ...:         self.height = 0 
    ...:  
    ...:     @property 
    ...:     def size(self): 
    ...:         return self.width, self.height 
    ...:      
    ...:     @size.setter 
    ...:     def size(self, size): 
    ...:         self.width, self.height = size 
    ...:  
    ...:      
    ...:     @size.deleter 
    ...:     def size(self): 
    ...:         del self.width,self.height 
    ...:                                                                                                         

In [37]:                                                                                                         

In [37]: r= Rectange()                                                                                           

In [38]: r.size = 3,4                                                                                            

In [39]: r.size                                                                                                  
Out[39]: (3, 4)

In [40]: del r.size                                                                                              

In [41]: r.height                                                                                                
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-41-9fec4be02cd3> in <module>
----> 1 r.height

AttributeError: 'Rectange' object has no attribute 'height'

 为什么不好用,首相你定义的方法名必须相同,经过我的测试@property必须用在读取参数身上,麻烦的是不能单独设置写入的property

In [47]: class Rectange: 
    ...:     def __init__(self): 
    ...:         self.width = 0 
    ...:         self.height = 0 
    ...:  
    ...:  
    ...:     def dsize(self): 
    ...:         return self.width, self.height 
    ...:  
    ...:     @property 
    ...:     def size(self, size): 
    ...:         self.width, self.height = size 
    ...:  
    ...:  
    ...:      
    ...:     def dsize(self): 
    ...:         del self.width,self.height 
    ...:                                                                                                         

In [48]: r = Rectange()                                                                                          

In [49]: r.size=3,4                                                                                              
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-49-08f82adabb51> in <module>
----> 1 r.size=3,4

AttributeError: can't set attribute

 

这个就是说,方法转函数限制条件很多,如果是读取的话,问题不大,如果是带参数写入,必须先定义一个读取,一个写入,不能单独把写入的方法改成属性复制。

总体使用下来,强烈推荐用property的类实例话对多方法进行合并成一个对象属性进行操作。

 

为什么property能够实行这个功能呢,那是因为__get__,__set__,__delete__魔方函数定义了描述符。

 

 

概念:描述符就是将某种特殊类型的类的实例指派给另一个类的属性。

In [56]: class MyDescriptor: 
    ...:     def __get__(self, instance, owner): 
    ...:         print('__get__output=====>', instance, owner,self) 
    ...:  
    ...:     def __set__(self, instance, value): 
    ...:         print('__set__output=====>' , instance, value) 
    ...:  
    ...:     def __delete__(self, instance): 
    ...:         print('__delete__output=====>', instance) 
    ...:  
    ...: class Test: 
    ...:     x = MyDescriptor() 
    ...:                                                                                                         

In [57]: t = Test()                                                                                              

In [58]: t.x                                                                                                     
__get__output=====> <__main__.Test object at 0x10e9ca710> <class '__main__.Test'> <__main__.MyDescriptor object at 0x10de16210>

In [59]: t                                                                                                       
Out[59]: <__main__.Test at 0x10e9ca710>

In [60]: t.x = 20                                                                                                
__set__output=====> <__main__.Test object at 0x10e9ca710> 20

In [61]: Test                                                                                                    
Out[61]: __main__.Test

 

其实从代码中可以看出来,在描述符里面任何一个参数里面都有这instance,这个instance就是用户描述符类属性实例化的对方,案例这里面就是t。

对x的属性进行读取,复制都会在描述符对应里面的__get__,__set__函数里面都将执行。

In [63]: class MyProperty: 
    ...:     def __init__(self, fget=None, fset=None, fdel=None): 
    ...:         self.fget = fget 
    ...:         self.fest = fset 
    ...:         self.fdel = fdel 
    ...:          
    ...:     def __get__(self, instance, owner): 
    ...:         return self.fget(instance) 
    ...:      
    ...:     def __set__(self, instance, value): 
    ...:         self.fest(instance, value) 
    ...:      
    ...:     def __delete__(self, instance): 
    ...:         self.fdel(instance) 
    ...:          
    ...: class Demo: 
    ...:     def __init__(self): 
    ...:         self.vv = None 
    ...:      
    ...:     def getvv(self): 
    ...:         return self.vv 
    ...:      
    ...:     def setvv(self,value): 
    ...:         self.vv = value 
    ...:      
    ...:     def delvv(self): 
    ...:         del self.vv 
    ...:      
    ...:     cute = MyProperty(getvv, setvv, delvv) 
    ...:                                                                                                         

In [64]: dd =Demo()                                                                                              

In [65]: dd.cute = 5                                                                                             

In [66]: dd.cute                                                                                                 
Out[66]: 5

In [67]: del dd.cute                                                                                             

In [68]: dd.cute                                                                                                 
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-68-b9d4fbd43102> in <module>
----> 1 dd.cute

<ipython-input-63-86b6cb623d25> in __get__(self, instance, owner)
      6 
      7     def __get__(self, instance, owner):
----> 8         return self.fget(instance)
      9 
     10     def __set__(self, instance, value):

<ipython-input-63-86b6cb623d25> in getvv(self)
     19 
     20     def getvv(self):
---> 21         return self.vv
     22 
     23     def setvv(self,value):

AttributeError: 'Demo' object has no attribute 'vv'

上面的代码展示了自己编写property的效果,跟官方的比较像了,上面定义了property针对不同的操作进行的操作返回不同的操作,property里面拥有需要操作的实例与传入的方法,所以定义具体的操作非常方便。

 

为了强化学习,我从一本书中抄写了一串代码强化记忆。

In [76]: class Celsius: 
    ...:     def __init__(self, value = 26): 
    ...:         self.value = float(value) 
    ...:  
    ...:     def __get__(self, instance, owner): 
    ...:         return self.value 
    ...:  
    ...:     def __set__(self, instance, value): 
    ...:         self.value = float(value) 
    ...:  
    ...: class Fahrenheri: 
    ...:     def __get__(self, instance, owner): 
    ...:         return instance.cel * 1.8 + 32 
    ...:  
    ...:     def __set__(self, instance, value): 
    ...:         instance.cel = (float(value) -32) /1.8     # 设置华氏度温度后,返回给实例复制cel,执行Celsius的s
    ...: et功能,并复制更新。 
    ...:                                                                                                         

In [77]: class Temperatrye: 
    ...:     cel = Celsius() 
    ...:     fah = Fahrenheri() 
    ...:                                                                                                         

In [78]: temp = Temperatrye()                                                                                    

In [79]: temp.cel                                                                                                
Out[79]: 26.0

In [80]: temp.fah                                                                                                
Out[80]: 78.80000000000001

In [81]: temp.fah = 250                                                                                          

In [82]: temp.cel                                                                                                
Out[82]: 121.11111111111111

 上面代码通过两个描述符对属性进行不同的切换,我觉的还是很有意思的。记号下。

 

 

补充一些Python学习笔记中的描述符定义:

class Descriptor:
    
    # 这个比__init__好用,都是在初始化的时候执行
    # 这个能获取到调用的类owner信息,已经类属性名name
    def __set_name__(self, owner, name):
        print(f'name: {owner.__name__}.{name}')
        self.name = f'__{name}__'

    def __get__(self, instance, owner):
        print(f'get: {instance}, {owner}')
        return getattr(instance, self.name, None)
    
    # setattr与.取值的效果一样,都会激活父类的__setattr__
    def __set__(self, instance, value):
        print(f'set {instance}, {value}')
        setattr(instance, self.name, value)
    
    def __delete__(self, instance):
        print(f'del: {instance}')
        raise AttributeError('Delete is disabled')

 

posted @ 2019-11-04 21:20  就是想学习  阅读(236)  评论(0编辑  收藏  举报