0%

Python之property函数、@property装饰器

这里介绍Python中的property函数、@property装饰器

abstract.png

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

# property函数: 创建一个名为my_size的属性
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------------------------------------------------")

# 通过property函数创建的属性操作,其本质依然是调用该属性所关联的方法
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)

figure 1.png

@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 装饰器:将size()方法 转换为 size属性的getter方法
# 被修饰方法 会在通过 <实例名>.size 访问 size属性 时被调用
@property
def size(self):
print("called get size method")
return self._size

# @<属性名>.setter 装饰器: 将size方法转换为 size属性的setter方法
# 被修饰方法 会在通过 <实例名>.size 修改 size属性 时被调用
@size.setter
def size(self, size):
if size<0 :
print("大小不能为负值")
return

print("called set size method")
self._size = size

# @<属性名>.deleter 装饰器:将size方法转换为 size属性的deleter方法
# 被修饰方法 会在通过 del <实例名>.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
# 通过property装饰器创建的属性操作,其本质依然是调用装饰器所关联的方法
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)

figure 2.png

由于被@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)

figure 3.png

参考文献

  1. Python编程·第3版:从入门到实践 Eric Matthes著
  2. Python基础教程·第3版 Magnus Lie Hetland著
  3. 流畅的Python·第1版 Luciano Ramalho著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝