Python教程35:类方法与静态方法

“工欲善其事,必先利其器。”

Python提供了三种方法类型:实例方法、类方法、静态方法。今天我们学习类方法(@classmethod)和静态方法(@staticmethod),理解它们的区别和使用场景。

1. 三种方法类型对比

实例方法(Instance Method)

我们最常用的方法:

 1class MyClass:
 2    def instance_method(self):
 3        """
 4        实例方法
 5        - 第一个参数是self(实例本身)
 6        - 可以访问实例属性和类属性
 7        - 通过实例调用
 8        """
 9        print(f"Called instance_method of {self}")
10        return "instance method"
11
12obj = MyClass()
13obj.instance_method()  # 正常调用

类方法(Class Method)

使用@classmethod装饰器:

 1class MyClass:
 2    @classmethod
 3    def class_method(cls):
 4        """
 5        类方法
 6        - 第一个参数是cls(类本身)
 7        - 可以访问类属性,不能访问实例属性
 8        - 通过类或实例调用
 9        """
10        print(f"Called class_method of {cls}")
11        return "class method"
12
13# 通过类调用
14MyClass.class_method()
15
16# 通过实例调用也可以
17obj = MyClass()
18obj.class_method()

静态方法(Static Method)

使用@staticmethod装饰器:

 1class MyClass:
 2    @staticmethod
 3    def static_method():
 4        """
 5        静态方法
 6        - 没有self或cls参数
 7        - 不能访问实例或类的状态
 8        - 通过类或实例调用
 9        - 就是普通函数,只是放在类的命名空间里
10        """
11        print("Called static_method")
12        return "static method"
13
14# 通过类调用
15MyClass.static_method()
16
17# 通过实例调用也可以
18obj = MyClass()
19obj.static_method()

对比总结

特性实例方法类方法静态方法
第一个参数selfcls
访问实例属性
访问类属性
调用方式实例类/实例类/实例
使用场景操作实例数据工厂方法、操作类数据工具函数

2. 类方法详解

基本用法

 1class Date:
 2    """日期类"""
 3    
 4    def __init__(self, year, month, day):
 5        self.year = year
 6        self.month = month
 7        self.day = day
 8    
 9    @classmethod
10    def from_string(cls, date_string):
11        """
12        替代构造函数(工厂方法)
13        - cls是Date类本身
14        - 可以创建实例
15        - 解析字符串创建对象
16        """
17        year, month, day = map(int, date_string.split('-'))
18        return cls(year, month, day)  # 调用__init__
19    
20    @classmethod
21    def today(cls):
22        """
23        获取今天的日期
24        - 类方法可以有多个
25        - 提供不同的实例化方式
26        """
27        import datetime
28        today = datetime.date.today()
29        return cls(today.year, today.month, today.day)
30    
31    def __str__(self):
32        return f"{self.year}-{self.month:02d}-{self.day:02d}"
33
34# 使用不同的构造方式
35date1 = Date(2024, 1, 15)           # 普通构造
36date2 = Date.from_string("2024-01-15")  # 类方法构造
37date3 = Date.today()                # 类方法构造
38
39print(date1)  # 2024-01-15
40print(date2)  # 2024-01-15
41print(date3)  # 当前日期

类方法的优势

  • 替代构造函数:提供多种实例化方式
  • 工厂方法:根据不同参数创建不同对象
  • 操作类属性:修改类级别的状态

类方法访问类属性

 1class Employee:
 2    """员工类"""
 3    
 4    # 类属性
 5    company_name = "TechCorp"
 6    employee_count = 0
 7    
 8    def __init__(self, name):
 9        self.name = name
10        Employee.employee_count += 1
11    
12    @classmethod
13    def get_employee_count(cls):
14        """
15        获取员工总数
16        - 访问类属性
17        - 不需要创建实例
18        """
19        return cls.employee_count
20    
21    @classmethod
22    def set_company_name(cls, name):
23        """
24        设置公司名称
25        - 修改类属性
26        - 影响所有实例
27        """
28        cls.company_name = name
29
30# 使用
31print(Employee.get_employee_count())  # 0
32
33emp1 = Employee("Alice")
34emp2 = Employee("Bob")
35
36print(Employee.get_employee_count())  # 2
37
38Employee.set_company_name("NewTech")
39print(Employee.company_name)  # NewTech

继承中的类方法

 1class Animal:
 2    """动物基类"""
 3    
 4    species_count = 0
 5    
 6    def __init__(self, name):
 7        self.name = name
 8        Animal.species_count += 1
 9    
10    @classmethod
11    def create_multiple(cls, names):
12        """
13        批量创建
14        - cls在子类中是子类本身
15        - 实现多态
16        """
17        return [cls(name) for name in names]
18
19class Dog(Animal):
20    """狗类"""
21    pass
22
23class Cat(Animal):
24    """猫类"""
25    pass
26
27# 创建多个实例
28dogs = Dog.create_multiple(["Buddy", "Max", "Charlie"])
29cats = Cat.create_multiple(["Whiskers", "Luna"])
30
31print([dog.name for dog in dogs])  # ['Buddy', 'Max', 'Charlie']
32print([cat.name for cat in cats])  # ['Whiskers', 'Luna']
33print(type(dogs[0]))  # <class '__main__.Dog'>(不是Animal)

3. 静态方法详解

基本用法

 1class MathUtils:
 2    """数学工具类"""
 3    
 4    @staticmethod
 5    def is_prime(n):
 6        """
 7        判断质数
 8        - 不需要访问类或实例属性
 9        - 纯工具函数
10        - 放在类里是为了组织代码
11        """
12        if n < 2:
13            return False
14        for i in range(2, int(n ** 0.5) + 1):
15            if n % i == 0:
16                return False
17        return True
18    
19    @staticmethod
20    def factorial(n):
21        """计算阶乘"""
22        if n <= 1:
23            return 1
24        return n * MathUtils.factorial(n - 1)
25
26# 使用(不需要创建实例)
27print(MathUtils.is_prime(17))  # True
28print(MathUtils.factorial(5))  # 120

静态方法的特点

  • 工具函数:与类相关但不需要访问类状态
  • 命名空间:组织相关函数
  • 语义清晰:表明函数属于这个类

静态方法vs普通函数

 1# 方式1:普通函数
 2def validate_email(email):
 3    return '@' in email
 4
 5# 方式2:静态方法
 6class User:
 7    @staticmethod
 8    def validate_email(email):
 9        """
10        验证邮箱
11        - 与User相关
12        - 但不需要访问User的属性
13        - 用静态方法组织代码更清晰
14        """
15        return '@' in email
16
17# 使用
18print(validate_email("test@example.com"))      # 方式1
19print(User.validate_email("test@example.com"))  # 方式2(更清晰)

何时用静态方法

  • 函数逻辑上属于这个类
  • 但不需要访问实例或类的状态
  • 想把相关函数组织在类的命名空间下

4. 实战示例

示例1:配置管理类

 1class Config:
 2    """配置管理类"""
 3    
 4    # 类属性:配置数据
 5    _config = {
 6        "debug": False,
 7        "database": "localhost",
 8        "port": 5432
 9    }
10    
11    @classmethod
12    def get(cls, key, default=None):
13        """
14        获取配置(类方法)
15        - 访问类属性
16        - 不需要创建实例
17        """
18        return cls._config.get(key, default)
19    
20    @classmethod
21    def set(cls, key, value):
22        """设置配置"""
23        cls._config[key] = value
24    
25    @classmethod
26    def load_from_file(cls, filename):
27        """
28        从文件加载配置(工厂方法)
29        - 类方法的典型应用
30        """
31        import json
32        with open(filename) as f:
33            cls._config = json.load(f)
34    
35    @staticmethod
36    def validate_port(port):
37        """
38        验证端口号(静态方法)
39        - 纯工具函数
40        - 与配置相关但不需要访问配置数据
41        """
42        return isinstance(port, int) and 0 < port < 65536
43
44# 使用
45print(Config.get("database"))  # localhost
46Config.set("debug", True)
47
48if Config.validate_port(8080):
49    Config.set("port", 8080)

示例2:日志类

 1import datetime
 2
 3class Logger:
 4    """日志类"""
 5    
 6    # 类属性:日志级别
 7    LOG_LEVEL = "INFO"
 8    
 9    def __init__(self, name):
10        """实例方法:特定模块的日志"""
11        self.name = name
12    
13    def log(self, message):
14        """实例方法:记录日志"""
15        timestamp = Logger.get_timestamp()
16        print(f"[{timestamp}] [{self.name}] {message}")
17    
18    @classmethod
19    def set_level(cls, level):
20        """类方法:设置全局日志级别"""
21        cls.LOG_LEVEL = level
22    
23    @staticmethod
24    def get_timestamp():
25        """
26        静态方法:获取时间戳
27        - 工具函数
28        - 不需要访问类或实例状态
29        """
30        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
31
32# 使用
33logger1 = Logger("Module1")
34logger2 = Logger("Module2")
35
36logger1.log("Starting")    # 实例方法
37logger2.log("Processing")
38
39Logger.set_level("DEBUG")  # 类方法
40print(Logger.get_timestamp())  # 静态方法

示例3:单例模式

 1class Database:
 2    """数据库连接(单例模式)"""
 3    
 4    _instance = None  # 类属性:单例实例
 5    
 6    def __init__(self, connection_string):
 7        if Database._instance is not None:
 8            raise Exception("Database is a singleton!")
 9        self.connection_string = connection_string
10        Database._instance = self
11    
12    @classmethod
13    def get_instance(cls):
14        """
15        获取单例实例(类方法)
16        - 确保只有一个实例
17        - 类方法的经典应用
18        """
19        if cls._instance is None:
20            cls._instance = cls("default_connection")
21        return cls._instance
22    
23    @classmethod
24    def reset_instance(cls):
25        """重置实例(主要用于测试)"""
26        cls._instance = None
27
28# 使用
29db1 = Database.get_instance()
30db2 = Database.get_instance()
31
32print(db1 is db2)  # True(同一个实例)

5. 选择指南

何时使用实例方法

1class BankAccount:
2    def __init__(self, balance):
3        self.balance = balance
4    
5    def deposit(self, amount):
6        """
7        需要修改实例状态 → 实例方法
8        """
9        self.balance += amount

何时使用类方法

 1class Person:
 2    count = 0
 3    
 4    def __init__(self, name):
 5        self.name = name
 6        Person.count += 1
 7    
 8    @classmethod
 9    def from_birth_year(cls, name, birth_year):
10        """
11        替代构造函数 → 类方法
12        """
13        age = 2024 - birth_year
14        return cls(name)
15    
16    @classmethod
17    def get_count(cls):
18        """
19        访问/修改类属性 → 类方法
20        """
21        return cls.count

何时使用静态方法

1class StringUtils:
2    @staticmethod
3    def is_palindrome(s):
4        """
5        工具函数,不需要访问类/实例状态 → 静态方法
6        """
7        return s == s[::-1]

6. 最佳实践

  1. 优先使用实例方法:默认选择
  2. 需要访问类属性时用类方法:如计数器、配置
  3. 工厂方法用类方法:替代构造函数
  4. 工具函数用静态方法:与类相关但独立的函数
  5. 避免滥用:不要为了用而用

7. 与Golang对比

Golang没有类的概念,但有类似的模式:

 1// Go:接收者方法
 2type Person struct {
 3    Name string
 4}
 5
 6// 值接收者方法(类似实例方法)
 7func (p Person) GetName() string {
 8    return p.Name
 9}
10
11// 指针接收者方法
12func (p *Person) SetName(name string) {
13    p.Name = name
14}
15
16// 包级函数(类似静态方法)
17func NewPerson(name string) *Person {
18    return &Person{Name: name}
19}

8. 小结

今天我们学习了类方法和静态方法:

  • 三种方法:实例方法、类方法、静态方法
  • 类方法:@classmethod,访问类属性,工厂方法
  • 静态方法:@staticmethod,工具函数
  • 选择指南:根据需要访问的状态选择
  • 实战应用:配置管理、日志、单例模式

掌握这三种方法类型,能让代码更清晰、更合理。


练习题

  1. 创建一个Temperature类,实现摄氏度和华氏度的转换(类方法)
  2. 实现一个计数器类,跟踪总共创建了多少个实例(类方法)
  3. 创建一个工具类,包含字符串验证的静态方法

思考题

为什么单例模式要用类方法而不是静态方法实现?


本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读