总结摘要
静态类 @staticmethod 和类方法 @classmethod 两个装饰器的基本使用场景。
刚刚拜读了一篇在 python 类中:
静态类 @staticmethod 和类方法 @classmethod 两个装饰器的基本使用场景。
收获良多,详细的分析可以阅读原文
本文记录下使用静态类和类方法,在使用的细节,以及自己一些思考。
一、静态类的定义与使用
在学习 @staticmethod 之前,一直都是用『私有方法』来表示『静态方法』
定义静态类
如代码所示的_add_two_string_num 方法,就是『私有方法』:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def _add_two_string_num(self, a, b):
"""在方法名前加一个下划线,用以表示私有方法"""
a_int = int(a)
b_int = int(b)
return a_int + b_int
def calc_age_after_n_year(self, n):
age = self.add_two_string_num(self.age, n)
print(f'{n}年以后,我{age}岁')
# 使用
>>> qiuyu = Person("core", "20")
>>> qiuyu.calc_age_after_n_year(10)
>>> 10年后,我30岁
|
用『静态类』改写一下,然后对比执行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@staticmethod
def add_two_string_num(a, b):
① """注意这里参数去掉了self"""
a_int = int(a)
b_int = int(b)
return a_int + b_int
def calc_age_after_n_year(self, n):
# 注意这里把 self 替换成了 Person 类名
② age = Person.add_two_string_num(self.age, n)
print(f'{n}年以后,我{age}岁')
# 使用
>>> qiuyu = Person("core", "20")
>>> qiuyu.calc_age_after_n_year(10)
>>> 10年后,我30岁
|
可以看到代码执行结果还是一样 ,分析一些注释部分发生了什么:
① 我在 add_two_string_num 方法的下划线,然后再头上戴了一个静态方法的『@staticmethod』的帽子,并去掉了self形参
② 戴上静态方法的帽子后,add_two_strinig_num 调用方法由 self.xxx 变成了Person.xxx, 使用类名调用该方法。
使用场景
仔细观察add_two_string_num这个静态类方法,可以发现它和 Person 这个类表示『人』这个抽象概念,没有什么关系(只是将两个字符串转成整型后相加)。
如果代码的其他类也会用到它,可以把它单独放到 Person 类外面,作为一个『工具函数』;
如果这个『工具函数』只有某一个类会用到,那么最好给它带上@staticmethod的帽子(装饰器)。
一句话总结:静态方法就是某个类专用的工具函数。
二、类方法的定义与使用
此『类方法』非彼『类的方法』,注意概念的区分
定义类方法
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
| import re
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce_myself(self):
print(f'大家好,我叫: {self.name}')
@staticmethod
def add_two_string_num(a, b):
... skip ...
① @classmethod
② def from_chinese_string(cls, sentence):
name = re.search('名字:(.*?),', content).group(1)
age = re.search('年龄:(\d+)', content).group(1)
③ return cls(name, age)
# 使用
>>> content = '我的名字:qiuyu,我的年龄:20,把它提取出来'
④ >>> qiuyu = People.from_chinese_string(content)
>>> qiuyu.introduce_myself()
>>> 大家好, 我叫: qiuyu
|
①『类方法』要戴上@classmethod的帽子,而定义一个普通的『类的方法』不用;
②『类方法』除了要戴帽子,self也要换成cls,这个参数其实就是People这个类本身;
③ 这里相当于People(name, age),但是还未实例化,并返回;
④ 在初次实例化People时,调用了类方法from_chinese_string,利用正则匹配__init__需要的name、age参数,完成;
这样做有什么好处呢?好处就在于我们完全不需要修改__init__
那么,也就不需要修改代码里面其它调用了People类的地方。
例如现在我又想增加从英文句子里面提取名字和年龄的功能,那么只需要再添加一个类方法就可以了:
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
31
| import re
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce_myself(self):
print(f'大家好,我叫: {self.name}')
@staticmethod
def add_two_string_num(a, b):
... skip ...
@classmethod
def from_chinese_string(cls, sentence):
name = re.search('名字:(.*?),', content).group(1)
age = re.search('年龄:(\d+)', content).group(1)
return cls(name, age)
@classmethod
def from_english_string(cls, sentence):
name = re.search('name: (.*?),', content).group(1)
age = re.search('age: (\d+)', content).group(1)
return cls(name, age)
# 使用
>>> content = 'My name:qiuyu,My age:20,hello'
>>> qiuyu = People.from_chinese_string(content)
>>> qiuyu.introduce_myself()
>>> 大家好, 我叫: qiuyu
|
使用场景
虽然可以额外写一些功能代码,去处理传入people类的初始化参数
但是在实际项目中,会使代码的可读性、维护性降低(试想在一页几千行的代码中,找到处理的函数…)
也不符合 pythonic,不是么?
结语
工作中 pycharm 总是提示我
Method ‘xxx’ may be ‘static’
今天终于明白了 pycharm 的良苦用心… …