首先先介绍一下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')