这里介绍Python中的property函数、@property装饰器
property函数
Python中的property函数可以将方法 与 属性(访问、修改)操作相关联,使得调用方法可以通过访问、修改属性的形式来实现。具体地:该函数接受四个可选的参数, 可以按需传入。其中,fget、fset、fdel、doc参数:分别代表对该属性的getter、setter、deleter、文档字符串的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Photo: def __init__(self, size=100): self.size = size def set_size(self, size): if size<0 : print("大小不能为负值") return
print("called set_size method") self.size = size def get_size(self): print("called get_size method") return self.size def del_size(self): print("called del_size method") self.size = None
my_size = property(get_size, set_size, del_size)
|
通过下面代码可以看到,通过property函数创建的my_size属性来操作时,其本质依然是调用该属性所关联的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| p1 = Photo(123) print("#1 p1 size: ", p1.get_size()) p1.set_size(456) print("#2 p1 size: ", p1.get_size()) p1.del_size() print("#3 p1 size: ", p1.get_size())
print("\n------------------------------------------------")
p1.my_size = 777 print("#4 p1 size: ", p1.my_size) p1.my_size = -111 del p1.my_size print("#5 p1 size: ", p1.my_size)
|
@property装饰器
除了使用property函数外,我们还可以使用@property装饰器来实现。示例如下。首先,在读取属性的方法上添加 @property 装饰器。此时,会生成一个与被修饰方法的方法名同名的属性。然后,将 @<属性名>.setter 装饰器、@<属性名>.deleter 装饰器 按需(不是必须的)添加到相应的修改属性、删除属性的方法上。
这里需要注意的是。对于setter、deleter方法而言,需要 被修饰的方法名 与 装饰器中的属性名 保持一致
- 例如 setter方法:size(self, size)方法的方法名size 与 @size.setter装饰器的属性名size 是一样的
- 例如 deleter方法:size(self)方法的方法名size 与 @size.delete装饰器的属性名size 是一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Video: def __init__(self, size): self._size = size
@property def size(self): print("called get size method") return self._size
@size.setter def size(self, size): if size<0 : print("大小不能为负值") return
print("called set size method") self._size = size @size.deleter def size(self): print("called del size method") self._size = None
|
测试结果如下所示
1 2 3 4 5 6 7 8 9 10 11
| v1 = Video(123) print("#1 v1 size: ", v1.size)
v1.size = 777 print("#2 v1 size: ", v1.size)
v1.size = -111
del v1.size print("#3 v1 size: ", v1.size)
|
由于被@property装饰器修饰的方法,会生成一个与被修饰方法的方法名同名的属性。故其不能与类中其他的实例属性重名,否则会引起无限递归。因为,通过<实例名>.size访问size属性时,首先会被转换为size(self)方法调用;而在执行 return self.size时,又视为访问self的size属性,于是又转换为size(self)方法调用。造成无限递归。故,一般会在实例属性名前加一个下划线_来避免重名。例如,上文我们将实例变量命名为_size
这里补充说明下,在Python中,对于以一个下划线_开头的实例属性而言,并不会限制外部直接对属性进行访问、修改。其旨在提醒开发者这是一个类内部的实例变量,应该尽量通过类对外暴露的方法来进行操作,避免在外部直接对该变量进行操作
1 2 3 4
| v2 = Video(100) print("#1 v2 size: ", v2._size) v2._size = 200 print("#2 v2 size: ", v2._size)
|
参考文献
- Python编程·第3版:从入门到实践 Eric Matthes著
- Python基础教程·第3版 Magnus Lie Hetland著
- 流畅的Python·第1版 Luciano Ramalho著