当前位置: 首页 > 科技观察

每日一技能:什么时候用静态方法和类方法?

时间:2023-03-19 02:21:15 科技观察

最近有同学在知识星球上提问,什么时候用静态方法,什么时候用类方法。今天我们就来看看这两种方式的应用场景。首先我们定义一个普通类,里面包含普通方法,普通方法也叫实例方法。classPeople:def__init__(self,name,age):self.name=nameself.age=agedefintroduce_myself(self):print(f'大家好,我的名字是:{self.name}')defadd_two_string_num(self,a,b):a_int=int(a)b_int=int(b)returna_int+b_intdefcalc_age_after_n_year(self,n):age=self.add_two_string_num(self.age,n)print(f'after{n}years,Iam{age}yearsold')该类运行效果如下图所示:请注意该类中的方法add_two_string_num,它接受两个参数,将其转换为int类型,然后相加并返回结果。这个过程很简单,但是它和People类有什么直接的关系吗?其实这个方法和这个类没有直接关系。我们甚至可以把它改成一个函数:defadd_two_string_num(a,b):a_int=int(a)b_int=int(b)returna_int+b_intclassPeople:def__init__(self,name,age):self.name=nameself.age=agedefintroduce_myself(self):print(f'大家好,我的名字是:{self.name}')defcalc_age_after_n_year(self,n):age=add_two_string_num(self.age,n)print(f'在{n}年后,我am{age}岁')kingname=People('kingname',20)kingname.introduce_myself()kingname.calc_age_after_n_year(10)的结果和之前完全一样:我们可以说add_two_string_num函数是一个效用函数.效用函数接收参数并输出结果,与调用它的人或在何处调用无关。但是现在有一个尴尬的事情,这个函数只有People调用,其他地方都没有调用。单独放在其他地方是多余的,做成实例方法浪费了self参数。这时候我们可以使用静态方法:classPeople:def__init__(self,name,age):self.name=nameself.age=agedefintroduce_myself(self):print(f'大家好,我叫:{self.name}')@staticmethoddefadd_two_string_num(a,b):a_int=int(a)b_int=int(b)returna_int+b_intdefcalc_age_after_n_year(self,n):age=People.add_two_string_num(self.age,n)print(f'{n}yearslater,Iam{age}yearsold')kingname=People('kingname',20)kingname.introduce_myself()kingname.calc_age_after_n_year(10)一句话总结:静态方法是一种工具函数,专用于某个班级。说完静态方法,再来说说类方法。什么情况下应该使用class方法呢?在回答这个问题之前,先问大家一个问题,People类如何初始化为实例?你不觉得不容易吗,就一行代码:xxx=People('xxx',10)注意这里初始化这个类的时候,一个一个传入参数。如果你用过顺丰发快递,你会发现在填写收件人的时候,有两种方式,一种方式和上面一样,一个一个填入参数。另一种方式,它给你一个输入框,你把一段包含姓名、地址、手机号码的文本粘贴进去,它会自动解析。所以,如果我现在给你一个字符串:我的名字:Qingnan,我的年龄:20,提取它。如何根据这个字符串生成People类的实例?这时候你可能会写:importrecontent='我的名字:庆南,我的年龄:20,提取出来'name=re.search('姓名:(.*?),',content).group(1)age=re.search('Age:(\d+)',content).group(1)kingname=People(name,age)确实有效,但是我可以让People类自动识别它吗?事实上,这是可能的。有两种方法。一种方法是在__init__中多加几个参数,然后在初始化时从这些参数中解析出来。这个方法大家都知道,我就不赘述了。再说第二种方法,就是使用类方法。我们只需要定义另一个类方法:importreclassPeople:def__init__(self,name,age):self.name=nameself.age=agedefintroduce_myself(self):print(f'大家好,我的名字是:{self.name}')@staticmethoddefadd_two_string_num(a,b):a_int=int(a)b_int=int(b)returna_int+b_int@classmethoddeffrom_chinese_string(cls,sentence):name=re.search('姓名:(.*?),',内容).group(1)age=re.search('年龄:(\d+)',content).group(1)returncls(name,age)defcalc_age_after_n_year(self,n):age=People.add_two_string_num(self.age,n)print(f'{n}年后,我{age}岁')content='我的名字:庆南,我的年龄:20,提取'kingname=People.from_chinese_string(content)kingname.introduce_myself()kingname.calc_age_after_n_year(10)运行效果如下图所示:类方法使用装饰器@classmethod进行装饰,其第一个参数为隐式参数cls。这个参数实际上就是People类本身。这个隐式参数在我们调用类方法的时候是不需要传入的。在这个类方法中,相当于用People初始化一个实例,然后返回这个实例。这样做有什么好处?好处是我们根本不需要修改__init__,所以我们不需要修改代码中其他调用People类的地方。比如现在我想增加从英文句子中提取名字和年龄的功能,那么只需要再增加一个类方法:importreclassPeople:def__init__(self,name,age):self.name=nameself.age=agedefintroduce_myself(self):print(f'大家好,我叫:{self.name}')@staticmethoddefadd_two_string_num(a,b):a_int=int(a)b_int=int(b)returna_int+b_int@classmethoddefrom_chinese_string(cls,sentence):name=re.search('姓名:(.*?),',content).group(1)age=re.search('年龄:(\d+)',content).group(1)returncls(姓名,年龄)@classmethoddeffrom_english_string(cls,句子):name=re.search('name:(.*?),',content).group(1)age=re.search('age:(\d+)',content).group(1)returncls(name,age)defcalc_age_after_n_year(self,n):age=People.add_two_string_num(self.age,n)print(f'{n}年后,我{age}岁old')content='myname:kinganme,myage:15pleaseextractthem'kingname=People.from_english_string(content)kingname.introduce_myself()kingname.calc_age_after_n_year(10)运行效果是sho下图中的wn:一句话总结:当你想使用工厂模式时,根据参数生成同一个类的不同对象时可以使用不同的Class方法。其实如果你用过Python自带的datetime模块,你会发现到处都是类方法:importdatetimenow=datetime.datetime.now()dt=datetime.datetime.fromtimestamp(1633691412)dt2=datetime.datetime.fromisoformat('2021-10-0819:10:05')这段代码中的.now()、.fromtimestamp()和.fromisoformat()都是类方法。它们都返回datetime.datetime对象,区别在于它们是根据不同类型的输入参数生成的。本文转载自微信公众号“闻所未闻的密码”,可通过以下二维码关注。转载本文请联系Code公众号。