A student on knowledge Planet asked me when to use static methods and when to use class methods. Today we will go through the application scenarios of these two methods.

First, let’s define a generic class. It’s full of ordinary methods. Common methods are also called instance methods.

class People:
    def __init__(self, name, age) :
        self.name = name
        self.age = age

    def introduce_myself(self) :
        print(F 'Hello everyone, my name is:{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}Years later, I{age}At the age of ')
Copy the code

This class looks like this:

Notice the add_two_string_num method in the class add_two_string_num, which takes two arguments, converts them to int, adds them together and returns the result. The process is very simple. But does it have anything to do directly with the People class?

This method has nothing to do with the class. We can even change it to a function:

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 'Hello everyone, my name is:{self.name}')

    def calc_age_after_n_year(self, n) :
        age = add_two_string_num(self.age, n)
        print(f'{n}Years later, I{age}At the age of ')

        
kingname = People('kingname'.20)
kingname.introduce_myself()
kingname.calc_age_after_n_year(10)
Copy the code

The result is exactly the same as before:

We can say that the add_two_string_num function is a utility function. The utility function takes the parameters and prints the results. It doesn’t care who is calling it or where.

But now there’s an awkward thing, this function, it’s only called by People, it’s not called anywhere else. It would be unnecessary to place it elsewhere, and making it an instance method would waste the self argument. In this case, we can use static methods:

class People:
    def __init__(self, name, age) :
        self.name = name
        self.age = age

    def introduce_myself(self) :
        print(F 'Hello everyone, my name is:{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}Years later, I{age}At the age of ')


kingname = People('kingname'.20)
kingname.introduce_myself()
kingname.calc_age_after_n_year(10)
Copy the code

To sum up: static methods are utility functions that are specific to a class.

Having said static methods, let’s talk about class methods. When should class methods be used? Before I answer that question, let me give you a question, how do I initialize People as an instance?

One line of code is all it takes:

xxx = People('xxx'.10)
Copy the code

Notice here, when you initialize this class, you’re passing in arguments one by one. If you have used SF Express to send express, you will find that there are two ways to fill in the addressee. One way is just like online, and you can fill in the addressee one by one. Another way, it gives you an input box. You paste a text containing your name, address, and phone number into it, and it automatically parses it.

So, if I now give you a string: my name: Qingnan, my age: 20, extract it. How do you generate an instance of the People class based on this string?

At this point, you might write something like:

import re
content = 'My name: Qingnan, my age: 20, pull it out'
name = re.search('name :(.*?) , ', content).group(1)
age = re.search('age :(\d+)', content).group(1)
kingname = People(name, age)
Copy the code

That’s fine, but can I make the People class auto-recognize? In fact, there are two ways to do this, one is to add a few more arguments to __init__, and then at initialization, parse from these parameters, this method is known, I won’t go into the details. Let’s talk about the second method, which is using class methods.

We only need to define a class method:

import re


class People:
    def __init__(self, name, age) :
        self.name = name
        self.age = age

    def introduce_myself(self) :
        print(F 'Hello everyone, my name is:{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('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}Years later, I{age}At the age of ')

        
content = 'My name: Qingnan, my age: 20, pull it out'
kingname = People.from_chinese_string(content)
kingname.introduce_myself()
kingname.calc_age_after_n_year(10)
Copy the code

The operating effect is shown in the figure below:

The classmethod is decorated with the decorator @classmethod, and its first argument is an implicit argument CLS. The argument is actually the People class itself. This implicit argument is not passed in when we call the class method. In this class method, you initialize an instance with People and then return it.

What’s the good of that? The advantage is that we don’t need to change __init__ at all, so we don’t need to change the rest of the code where People is called. For example, now THAT I want to add the ability to extract names and ages from English sentences, ALL I need to do is add another class method:

import re


class People:
    def __init__(self, name, age) :
        self.name = name
        self.age = age

    def introduce_myself(self) :
        print(F 'Hello everyone, my name is:{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('name :(.*?) , ', content).group(1)
        age = re.search('age :(\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}Years later, I{age}At the age of ')

        
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)
Copy the code

The operating effect is shown in the figure below:

In a word: You can use class methods when you want to use the factory pattern to generate different objects of the same class with different parameters.

In fact, if you’ve used Python’s built-in Datetime module, you’ll notice that class methods are everywhere:

import datetime

dt0 = datetime.datetime(2021.10.9.19.10.5)
now = datetime.datetime.now()
dt = datetime.datetime.fromtimestamp(1633691412)
dt2 = datetime.datetime.fromisoformat('the 2021-10-08 19:10:05')
Copy the code

The.now(),.fromtimestamp() and.fromisoformat() in this code are all class methods. They all end up returning datetime.datetime objects. But they are generated from different types of input parameters.