原文地址:https://www.cnblogs.com/yeer-xuan/p/13488291.html
问题引出
在业务处理时经常需要在数据的读取和存入前对数据进行预处理,通过@property和@*.setter两个装饰器就可以方便的实现。
@property
python中的@property装饰器可以总结为两个作用:
- 让函数可以像普通变量一样使用
- 对要读取的数据进行预处理
示例1
我们先编写一个测试类:
1 2 3 4 5 6 |
class User(): def __init__(self, name, age): self.name = name self.age = age |
假设现在我们需要将name与age合并成一个新属性tag,在不使用@property和修改类属性的情况下我们可能会这样写:
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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的情况下我们可能会这样写:
1 2 3 4 5 6 7 8 9 10 11 12 |
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我们就可以更方便的进行预处理操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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装饰器我们可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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装饰器,我们可以方便的设置只读属性,即无法对属性进行修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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的组合使用我们就可以实现密码的密文存储和明文输出,具体步骤为:用户输入明文->转化为密文后存入->用户读取时先转化为明文再输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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对应的函数,因此我们可以将其用于数据的预处理。