目录
__get__(self, instance, owner):
__set__(self, instance, value):
1. 描述符基础
描述符(descriptor),就是将某种特殊类型的类的实例对象,赋另一个类的属性;
描述符方法,包括 __get__、__set__、__delete__ 三种魔法方法,只有当定义它们的一个类(descriptor类)的实例对象被赋给另一个类(owner类)的类属性时使用;
前文所学习的 property() 函数,实际上就是一个包含了三种描述符方法的描述符类;
注意描述符的实例对象,需要赋给类属性,访问实例层次上的描述符只会返回描述符本身,无法自动调用 __get__ 和 __set__ 方法。
#创建描述符类
class TestDescriptor:
def __init__(self, nameinfo):
self.nameinfo = nameinfo
def __get__(self, instance, owner):
return self.nameinfo
#创建将描述符类实例对象赋给实例属性的类
class TestOwner:
def __init__(self, nameinfo2):
self.nameinfo2 = nameinfo2
self.x = TestDescriptor(self.nameinfo2)
#调用由描述符类赋值的实例属性,不能正确调用魔法方法
testx = TestOwner('test')
testx.x
<__main__.TestDescriptor object at 0x00000238691615B0>
testx.x.__dict__
{'nameinfo': 'test'}
testx.x.nameinfo
'test'
#创建将描述符类实例对象赋给类属性的类
class TestOwner2:
nameinfo2 = 'test'
x = TestDescriptor(nameinfo2)
#调用由描述符类赋值的类属性,正确调用魔法方法
testy = TestOwner2()
testy.y
'test'
2. 描述符方法
2.1 方法简述
__get__(self, instance, owner):
获取 owner 类或 owner 的实例(instance)属性,返回属性的值;
__set__(self, instance, value):
设置 owner类的一个实例对象(instance)的属性为新的 value,没有返回值;
__delete__(self, instance):
删除 owner类的一个实例对象(instance)的属性,没有返回值;
2.2 方法参数
描述符方法的各个参数说明:
- self:descriptor类,即描述符类的实例对象;
- instance:owner类的实例对象
- owner:owner类
- value:用于设置属性的值
2.3 简单示例
如下示例,简单表现描述符类的使用,以及描述符方法各个参数实际调用对象:
#创建 descriptor 类,展示各个参数
class TestDescriptor:
def __get__(self, instance, owner):
print(f'__get__方法各参数值为,self:{self},instance:{instance},owner:{owner}')
def __set__(self, instance, value):
print(f'__set__方法各参数值为,self:{self},instance:{instance},value:{value}')
def __delete__(self, instance):
print(f'__delete__方法各参数值为,self:{self},instance:{instance}')
#创建 Owner 类,Owner 类中创建 descriptor 类的实例作为 Owner 类的一个属性
class TestOwner:
ins_D = TestDescriptor()
#创建 Owner 类的实例
ins_O = TestOwner()
#Owner 类和类的实例对象
ins_O
<__main__.TestOwner object at 0x0000024E5B4E4F40>
TestOwner
<class '__main__.TestOwner'>
#对 Owner 类中属性,也即 descriptor 类的实例作设置,调用 descriptor 类的 __set__ 方法
class TestOwner:
ins_O.ins_D = 'Testinfo: value'
__set__方法各参数值为,self:<__main__.TestDescriptor object at 0x0000024E5B4A2670>,instance:<__main__.TestOwner object at 0x0000024E5B4E4F40>,value:Testinfo: value
#对 Owner 类中属性,也即 descriptor 类的实例作读取,调用 descriptor 类的 __get__ 方法
ins_O.ins_D
__get__方法各参数值为,self:<__main__.TestDescriptor object at 0x0000024E5B4A2670>,instance:<__main__.TestOwner object at 0x0000024E5B4E4F40>,owner:<class '__main__.TestOwner'>
#对 Owner 类中属性,也即 descriptor 类的实例作删除,调用 descriptor 类的 __delete__ 方法
del ins_O.ins_D
__delete__方法各参数值为,self:<__main__.TestDescriptor object at 0x0000024E5B4A2670>,instance:<__main__.TestOwner object at 0x0000024E5B4E4F40>
3. 操作实例
property 函数自行实现实操:
#通过描述符类实现的类似 property 函数的 TestProperty 类
class TestProperty:
def __init__(self, getdef, setdef, deldef):
self.getdef = getdef
self.setdef = setdef
self.deldef = deldef
def __get__(self, instance, owner):
return self.getdef(instance)
def __set__(self, instance, value):
self.setdef(instance,value)
def __delete__(self, instance):
self.deldef(instance)
#调用描述符类:TestProperty,实现类似 property 函数效果
class TestOwner:
def __init__(self, info = 'testinfo'):
self.info = info
def getinfo(self):
return self.info
def setinfo(self, newinfo):
self.info = newinfo
def delinfo(self):
del self.info
ins_D = TestProperty(getinfo, setinfo, delinfo)
#创建 TestOwner 的实例对象
ins_O = TestOwner()
#调用实例对象 ins_O 属性 ins_D,也即 TestProperty 的实例对象
#获取 ins_O.ins_D 属性
ins_O.ins_D
'testinfo'
#设置 ins_O.ins_D 属性,并查询
ins_O.ins_D = 'Hello, World!'
ins_O.ins_D
'Hello, World!'
#删除 ins_O.ins_D 属性,并验证删除成功
del ins_O.ins_D
ins_O.ins_D
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 8, in __get__
File "<input>", line 21, in getinfo
AttributeError: 'TestOwner' object has no attribute 'info'
温度类实操,要求如下:
- 定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性;
- 要求两个属性会自动转换,即可以给摄氏度属性赋值,获取华氏度则自动转换刚才赋值的摄氏度;
#创建摄氏度描述符类
class Celsius:
def __init__(self, value=0.0):
self.value = float(f'{value:.2f}')
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(f'{value:.2f}')
#创建华氏度描述符类
class Fahrenheit:
def __get__(self, instance, owner):
return (instance.cel * 1.8) + 32
def __set__(self, instance, value):
tempcel = (float(value) - 32) / 1.8
instance.cel = float(f'{tempcel:.2f}')
#创建温度类
class Temperature:
cel = Celsius()
fah = Fahrenheit()
#创建温度类的实例
tempx = Temperature()
#通过温度类的实例,对类属性 cel/fah 做读取、设置,调用魔法方法成功
tempx.cel
0.0
tempx.cel = 25
tempx.cel
25.0
tempx.fah
77.0
tempx.fah = 100
tempx.cel
37.78