问题引出
在业务处理时经常需要在数据的读取和存入前对数据进行预处理,通过@property和@*.setter两个装饰器就可以方便的实现。
@property
python中的@property装饰器可以总结为两个作用:
- 让函数可以像普通变量一样使用
- 对要读取的数据进行预处理
示例1
我们先编写一个测试类:
class User():
def __init__(self, name, age):
self.name = name
self.age = age
假设现在我们需要将name与age合并成一个新属性tag,在不使用@property和修改类属性的情况下我们可能会这样写:
class User():
def __init__(self, name, age):
self.name = name
self.age = age
def tag(self):
return self.name + str(self.age)
user = User('xiao',5)
print(user.tag())
这种方式自然没有问题,但是tag与其说是一个函数其实更像是user对象的一个属性,用user.tag()的方法获取显得不那么自然,而使用@property装饰器我们就可以用user属性的方式来得到tag:
class User():
def __init__(self, name, age):
self.name = name
self.age = age
@property
def tag(self):
return self.name + str(self.age)
user = User('xiao',5)
print(user.tag)
对于tag来说使用类属性 user.tag 的方式获取会比 user.tag() 更加的合理。
示例2
我们使用和上文一样的测试类,现在我们需要在读取user的age属性时必须在原来的基础上+5(预处理),但是不能修改类中保存的age值,在不使用@property的情况下我们可能会这样写:
class User():
def __init__(self, name, age):
self.name = name
self._age = age
def age(self):
return self.age + 5
user = User('xiao',5)
print(user.age())
和示例1的问题相同,我们需要用函数调用的方式来获取预处理后的age值,而使用@property我们就可以更方便的进行预处理操作:
class User():
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age + 5
user = User('xiao',5)
print(user.age)
我们先修改类属性age为_age,然后为age方法加上@property装饰器,这样我们在外面就可以用user.age方便的获取预处理后的值了。
通过@property我们实现了在读取类属性前对数据进行预处理,那么我们能不能在数据从外部存入类属性前对数据进行预处理呢?
@*.setter
python中的@*.setter装饰器可以总结为两个作用:
- 对要存入的数据进行预处理
- 设置可读属性(不可修改)
注意:@*.setter装饰器必须在@property装饰器的后面,且两个被修饰的函数的名称必须保持一致,* 即为函数名称。
示例1
我们使用和上文一样的测试类,现在我们需要在数据存入user.age前先对其+5,利用@*.setter装饰器我们可以这样写:
class User():
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age
@age.setter
def age(self,n):
self._age = n + 5
user = User('xiao',0)
user.age = 5
print(user.age)
# 结果:10
当执行 user.age = 5 时,@age.setter装饰器下的age函数会将数据+5后再存入类属性_age中,实现了存入前对数据的预处理。
示例2
通过@*.setter装饰器,我们可以方便的设置只读属性,即无法对属性进行修改:
class User():
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age
@age.setter
def age(self,n):
print('数据不能被修改')
user = User('xiao',0)
user.age = 5
在为age赋值时会立即触发@age.setter装饰器下的age函数中的提示语句或者异常处理语句。
综合示例
通过@*.setter和@property的组合使用我们就可以实现密码的密文存储和明文输出,具体步骤为:用户输入明文->转化为密文后存入->用户读取时先转化为明文再输出。
class User():
def __init__(self, name):
self.name = name
self._password = '' # 密文存储
@property
def password(self):
return decryption(self._password) # 解密
@password.setter
def password(self,word):
self._password = encryption(word) # 加密
user = User('xiao')
user.password = '123' #明文输入
print(user.password) #明文输出
总结:为两个同名函数打上@*.setter装饰器和@property装饰器后,当把函数作为变量赋值时会触发@*.setter对应的函数,当把函数作为变量读取时会触发@property对应的函数,因此我们可以将其用于数据的预处理。