关于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')
浙公网安备 33010602011771号