装饰器是Python中很重要的一个概念,但是这篇文档不介绍装饰器的实现,我们只需要知道装饰器可以实现一些功能;
Python中也包含一些自带的装饰器,这次就说一下属性装饰器property
相关知识点
用法
property装饰器的作用是让一个类方法的访问方式变成属性的访问方式,用法如下:
class Num:
@property
def x(self):
return '用类方法定义,用property装饰的x'
a = Num() # 把C实例化,命名为a
print(a.x) # 我们访问了a实例的x方法,但是没有对x做调用,和访问实例属性的方法一样
输出:用类方法定义,用property装饰的x
作用
使用这种方式来定义实例属性,我们就可以在类属性x中对x做一定的校验,这一点我们在后面再说。
衍生装饰器
举一个数字相加的例子:
class Num:
@property
def x(self):
return 1
@property
def y(self):
return 2
def printf(self):
print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
a = Num() # 把C实例化,命名为a
a.printf()
这个例子和上面的例子一样,我们定义了属性x和y,输出了x+y的和
这个例子的输出是:1加2的和是:3
这个时候我们希望可以在外部修改x的值,就会发现无法修改,因为本质上x是一个方法,而不是类属性:
class Num:
@property
def x(self):
return 1
@property
def y(self):
return 2
def printf(self):
print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
a = Num() # 把C实例化,命名为a
a.x = 2
print(a.x)
输出:AttributeError: can't set attribute
所以我们需要另外在类中定义一个方法,来修改x的值,相当于给外部创建一个API接口,让外部通过这个API来修改,这就用到了setter装饰器:
setter装饰器
用法
为了可以修改x,我们把x的值1写成实例属性self.num,
当然这个例子中外部也可以直接通过修改num的值来修改x的值,不过这个不要紧,因为只是为了方便理解,
实际使用中我们可以把self.num 写成私有属性,来保障不会被外部直接修改值,
写一个x属性同名方法,并且用x属性同名的setter装饰器(x.setter)来装饰同名方法x,然后在这个方法下实现对self.num的值的修改:
class Num:
def __init__(self):
self.num = 1
@property
def x(self):
return self.num
@x.setter
def x(self, item):
self.num = item
@property
def y(self):
return 2
def printf(self):
print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
a = Num() # 把C实例化,命名为a
a.x = 2
a.printf()
输出:2加2的和是:4
这样我们就可以通过a.x = 2来给self.num重新赋值,这样做的好处是不改变外层修改x值的方式,来实现对property属性的修改,这也是让代码更高可用,更可读很重要的一点。
deleter装饰器
用法
同setter装饰器,不再详细说明,不同的地方在于被deleter装饰的同名x方法不接收参数,因为我们只是删除了self.num,不需要接收参数:
class Num:
def __init__(self):
self.num = 1
@property
def x(self):
return self.num
@x.setter
def x(self, item):
self.num = item
@x.deleter
def x(self):
del self.num
@property
def y(self):
return 2
def printf(self):
print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
a = Num() # 把C实例化,命名为a
a.x = 2
a.printf()
del a.x
print(a.x)
输出:
2加2的和是:4
AttributeError: 'Num' object has no attribute 'num'
deleter装饰器装饰的方法就可以被使用del 来删除对应的属性,删除属性的操作我们就再同名方法x中进行了实现,
这样外部删除属性的方法也和删除一个属性的方法保持一致,同样保证了高可用性和可读性。
作用
现在可以说明文章一开始说的作用了,
我们通过这种property,setter,deleter装饰器的方式,把实例属性self.num写成property属性,我们就可以完成一个操作,
对外部修改、删除属性的操作,做进一步的过滤和限制
还是同样的例子,我们希望可以限制上层代码实例化并且修改x的值为非int类型,因为这会造成我们的求和方法报错,我们就可以通过这种方式实现:
class Num:
def __init__(self):
self.num = 1
@property
def x(self):
return self.num
@x.setter
def x(self, item):
if isinstance(item, int):
self.num = item
else:
print('不要把非int类型赋值给x!!!')
@property
def y(self):
return 2
def printf(self):
print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
a = Num() # 把C实例化,命名为a
a.x = '1234567' # 突发奇想把x的值改为str类型
输出:不要把非int类型赋值给x!!!
综上,这样就可以保障上层在修改x的值的时候,可以被我们的同名装饰器x先做进一步校验,
当然你想要先对入参item做一些其他操作也一样可以,操作之后,再更改self.num为item,
这个时候printf 求和方法,就可以安全使用 + 运算符求和了。
总结
使用property装饰器装饰的方法可以被上层以调用属性的方式来调用,类本身也可以,可以理解为property属性,也是一个属性;
使用property属性同名.setter装饰器装饰的同名方法,提供property=xx 的方法;
使用property属性同名.deleter装饰器装饰的同名方法,提供del xx的方法。
另外,重要的一点是既然我们已经使用property来代替实例属性,那自然我们是不希望外层可以直接修改实例属性的,
既然如此我们就最好不能使用上述例子中使用的self.num的命名方式来定义实例属性,而是使用私有属性的方式来定义(关于私有属性的知识点可以查看文章开头的相关知识点):
class Num:
def __init__(self):
self.__num = 1
@property
def x(self):
return self.__num
@x.setter
def x(self, item):
if isinstance(item, int):
self.__num = item
else:
print('不要把非int类型赋值给x!!!')
@x.deleter
def x(self):
if self.__num:
del self.__num
else:
print('属性不存在')
@property
def y(self):
return 2
def printf(self):
print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
a = Num() # 把C实例化,命名为a
a.__name = 3 # 修改私有属性为3
a.printf()
这样外部就不能通过 a.__name = 3的方式来改变私有属性__name,如果需要修改,就要通过a.x = 3的“API”来修改,从而我们可以对“API入参”进行校验和处理。