一日一技:什么情况使用静态方法和类方法?

简介: 一日一技:什么情况使用静态方法和类方法?

什么情况下使用静态方法,什么情况下使用类方法。今天我们就来捋一下这两个方法的应用场景。


首先,我们来定义一个普通的类,里面都是普通的方法,普通方法又叫实例方法。


class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def introduce_myself(self):
        print(f'大家好,我叫: {self.name}')
    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}岁')


这个类运行起来的效果如下图所示:


1.png


大家注意在这个类里面的方法add_two_string_num,它接受两个参数,并将他们转换为int类型,然后相加并返回结果。这个过程非常简单,但是,它跟People这个类有什么直接关系吗?


其实这个方法跟这个类没有什么直接关系,我们甚至把它改成函数都可以:


def add_two_string_num(a, b):
    a_int = int(a)
    b_int = int(b)
    return a_int + b_int
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def introduce_myself(self):
        print(f'大家好,我叫: {self.name}')
    def calc_age_after_n_year(self, n):
        age = add_two_string_num(self.age, n)
        print(f'{n}年以后,我{age}岁')
kingname = People('kingname', 20)
kingname.introduce_myself()
kingname.calc_age_after_n_year(10)


运行结果跟之前完全一样:


2.png


我们可以说,add_two_string_num函数就是一个工具函数。工具函数接收参数,输出结果,完全不关心谁在调用他,也不关心在哪里调用他。


但现在有一个比较尴尬的事情,这个函数,只有 People在调用,其它地方都没有调用。单独把它放到其它地方又显得多余,弄成实例方法又浪费了self参数,这个时候,我们就可以用静态方法:


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):
        a_int = int(a)
        b_int = int(b)
        return a_int + b_int
    def calc_age_after_n_year(self, n):
        age = People.add_two_string_num(self.age, n)
        print(f'{n}年以后,我{age}岁')
kingname = People('kingname', 20)
kingname.introduce_myself()
kingname.calc_age_after_n_year(10)


一句话总结:静态方法就是某个类专用的工具函数。


说完了静态方法,我们再说说类方法。什么情况下应该使用类方法呢?回答这个问题前,我先反问你一个问题,怎么把People类初始化成一个实例?


你说这还不简单吗,一行代码就行了啊:


xxx = People('xxx', 10)


注意,这里你在初始化这个类的时候,你是一个一个参数传入进去的。如果你用过顺丰寄送快递,你就会发现,填写收件人的时候,有两种方式,一种方式就像上面这样,一个一个参数填进去。另一种方式,它给你一个输入框,你把一段包含姓名,地址,手机号的文字粘贴进去,它自动解析。


那么,如果我现在给你一个字符串:我的名字:青南,我的年龄:20,把它提取出来。你怎么基于这个字符串生成People类的实例?


这个时候,你可能会这样写:


import re
content = '我的名字:青南,我的年龄:20,把它提取出来'
name = re.search('名字:(.*?),', content).group(1)
age = re.search('年龄:(\d+)', content).group(1)
kingname = People(name, age)


这样做确实可以,但我能不能让People这个类自动识别呢?其实是可以的,有两种方法,一种方法是在__init__里面多加几个参数,然后在初始化的时候,从这几个参数里面解析,这个方法大家都知道,我就不多讲了。我们来讲讲第二个方法,就是使用类方法。


我们只需要再定义一个类方法:


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):
        a_int = int(a)
        b_int = int(b)
        return a_int + b_int
    @classmethod
    def from_chinese_string(cls, sentence):
        name = re.search('名字:(.*?),', content).group(1)
        age = re.search('年龄:(\d+)', content).group(1)
        return cls(name, age)
    def calc_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)


运行效果如下图所示:


3.png


类方法使用装饰器@classmethod来装饰,并且它的第一个参数是隐式参数cls。这个参数其实就是People这个类本身。这个隐式参数在我们调用类方法的时候,是不需要传入的。在这个类方法里面,相当于使用People初始化了一个实例,然后把这个实例返回了出去。


这样做有什么好处呢?好处就在于我们完全不需要修改__init__,那么,也就不需要修改代码里面其它调用了People类的地方。例如现在我又想增加从英文句子里面提取名字和年龄的功能,那么只需要再添加一个类方法就可以了:


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):
        a_int = int(a)
        b_int = int(b)
        return a_int + b_int
    @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)
    def calc_age_after_n_year(self, n):
        age = People.add_two_string_num(self.age, n)
        print(f'{n}年以后,我{age}岁')
content = 'my name: kinganme, my age: 15 please extract them'
kingname = People.from_english_string(content)
kingname.introduce_myself()
kingname.calc_age_after_n_year(10)



运行效果如下图所示:


4.png


一句话总结:当你想使用工厂模式,根据不同的参数生成同一个类的不同对象的时候,就可以使用类方法。


其实如果大家使用过Python自带的datetime模块,你就会发现类方法无处不在:


import datetime
now = datetime.datetime.now()
dt = datetime.datetime.fromtimestamp(1633691412)
dt2 = datetime.datetime.fromisoformat('2021-10-08 19:10:05')


这段代码里面的.now().fromtimestamp().fromisoformat(),都是类方法。他们最终返回的都是datetime.datetime对象,区别在于他们是根据不同类型的输入参数生成的。


请关注微信公众号【未闻Code】获取更多精彩文章。

目录
相关文章
|
9月前
|
C#
C# 继承类中(父类与子类)构造函数的调用顺序
C# 继承类中(父类与子类)构造函数的调用顺序
|
10月前
|
Java
Java 类(私有属性、对象方法、类方法,构造函数)
Java 类(私有属性、对象方法、类方法,构造函数)
93 0
|
11月前
|
程序员
为什么子类会调用父类无参的构造函数
为什么子类会调用父类无参的构造函数
|
Java
16 类变量(静态变量)与类方法(静态方法)
1 .静态变量 静态变量指在类中被static修饰的变量
80 0
|
开发者 Python
类方法和静态方法 | 学习笔记
快速学习类方法和静态方法,介绍了类方法和静态方法系统机制, 以及在实际应用过程中如何使用。
88 0
类方法和静态方法 | 学习笔记
|
开发者 Python
类方法和静态方法回顾|学习笔记
快速学习类方法和静态方法回顾
82 0
类方法和静态方法回顾|学习笔记
|
C++
C++类的静态方法
C++类的静态方法
67 0
|
Java C++
C++类中在构造器中调用本类的另外构造器
C++类中在构造器中调用本类的另外构造器
83 0
|
前端开发 开发者
class-子类访问父类上的实例方法|学习笔记
快速学习 class-子类访问父类上的实例方法
88 0
子类到底能不能继承父类的私有属性?
继承就像是我们现实生活中的父子关系,儿子可以遗传父亲的一些特性,在面向对象语言中,就是一个类可以继承另一个类的一些特性,从而可以代码重用,其实继承体现的是is-a关系,父类同子类在本质上还是一类实体;子类通过继承父类的属性的行为,我们称之为继承。Java只支持单继承,不支持多继承。因为多继承容易带来安全隐患:当多个父类定义相同的功能,当功能内容不同的时候,子类对象不确定要运行哪一个,在Java中用另一种形式体现出来,就是接口的多实现。