【Python】修饰器总结

总结一些遇到过的有意思的修饰器。

@property

将方法变成属性调用。

一般类及问题

  • 有这样一个类,能够展现一个陌生人的基本信息。
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
class Stranger(object):
def __init__(self, gender=None, age=None, job=None):
self.gender = gender
self.age = age
self.jobb = job


if __name__ == "__main__":
# 创建一个“妹子”
meizi = Stranger()

# 设置妹子的属性
meizi.gender = "female"
meizi.age = 18
meizi.job = "teacher"

# 访问妹子的属性
print("妹子信息:")
print("性别:{gender}".format(gender=meizi.gender))
print("年龄:{age}".format(age=meizi.age))
print("职业:{job}".format(job=meizi.job))

# 输出:
#妹子信息:
#性别:female
#年龄:18
#职业:teacher
  • 但可能会产生一个问题:输出的结果异常
    • 原因是属性的值得范围并未做明确的设定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ...

if __name__ == "__main__":
# 创建一个“妹子”
meizi = Stranger()

# 设置妹子的属性
meizi.gender = "beijing"
meizi.age = "teacher"
meizi.job = 20

# 访问妹子的属性
print("妹子信息:")
print("性别:{gender}".format(gender=meizi.gender))
print("年龄:{age}".format(age=meizi.age))
print("职业:{job}".format(job=meizi.job))

# 输出:
#妹子信息:
#性别:beijing
#年龄:teacher
#职业:20

普通解决方案

  • 一个比较简单的解决方案就是对值的范围进行设定。
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
class Stranger(object):
def __init__(self, gender=None, age=None, job=None):
self.gender = gender
self.age = age
self.jobb = job

# 设置age
def set_age(self, age):
if isinstance(age, int):
self.age = age
else:
raise ValueError("'int' type need")

# 读取age
def get_age(self):
return self.age


if __name__ == "__main__":
# 创建一个“妹子”
meizi = Stranger()

meizi.set_age(18)
print("年龄:{age}".format(age=meizi.get_age()))

# 输出:
#年龄:18
  • 此时如果值的范围不对,会报错。
1
2
3
meizi.set_age("teacher")
print("年龄:{age}".format(age=meizi.get_age()))
# ValueError: 'int' type need
  • 此时我们解决了这个问题,那么有没有更加简便的方法呢?

引入@property

  • 为了避免麻烦,于是有了@property装饰器。它的作用是:将方法变成属性调用。
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
29
class Stranger(object):
def __init__(self, gender=None, age=None, job=None):
self.gender = gender
self._age = age # 这里的成员属性_age需要与成员方法age()区分开
self.jobb = job

# 读取age
@property # 实现一个age相关的getter方法
def age(self):
return self._age

# 设置age
@age.setter # 实现一个age相关的setter方法
def age(self, value):
if isinstance(value, int):
self._age = value
else:
raise ValueError("'int' type need")


if __name__ == "__main__":
# 创建一个“妹子”
meizi = Stranger()

meizi.age = 18 # 使用时注意是.age,不是._age
print("年龄:{age}".format(age=meizi.age))

# 输出:
#年龄:18
  • 注意!
    • 属性名与方法名一定要区分开,不然会进入死循环
      • (self._age,def age())
    • 实例化的对象使用属性时
      • 不是调用属性(meizi._age),而是用的方法名(meizi.age)
    • @property 其实就是实现了 getter 功能;
      • @xxx.setter 实现的是 setter 功能;
      • 还有一个 @xxx.deleter 实现删除功能
    • 定义方法的时候 @property 必须在 @xxx.setter之前,
      • 且二者修饰的方法名相同(age())
    • 如果只实现了 @property(而没有实现 @xxx.setter),那么该属性为只读属性

@property原理

函数接口:property(fget=None, fset=None, fdel=None, doc=None)

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
29
30
class Stranger(object):
def __init__(self, gender=None, age=None, job=None):
self.gender = gender
self._age = age
self.jobb = job

# 设置_age
def set_age(self, age):
if isinstance(age, int):
self._age = age
else:
raise ValueError("'int' type need")

# 读取_age
def get_age(self):
return self._age

# 使得实例化对象可以利用.age方式来访问
age = property(get_age, set_age)


if __name__ == "__main__":
# 创建一个“妹子”
meizi = Stranger()

meizi.age = 18
print("年龄:{age}".format(age=meizi.age))

# 输出:
#年龄:18

参考

  • [@property的使用](https://blog.csdn.net/qq_41359051/article/details/82939655)
  • 使用@property