杰瑞科技汇

Python中classmethod与普通方法有何区别?

核心概念

@classmethod 是一个装饰器(Decorator),用于将一个方法声明为“类方法”,这意味着这个方法与类本身相关联,而不是与类的某个特定实例(对象)相关联。

Python中classmethod与普通方法有何区别?-图1
(图片来源网络,侵删)

类方法的第一个参数不是实例(self),而是类本身(通常命名为 cls)。


语法

class MyClass:
    @classmethod
    def my_class_method(cls, arg1, arg2, ...):
        # 方法体
        # 'cls' 代表 MyClass 这个类
        pass
  • @classmethod: 装饰器,告诉 Python 这是一个类方法。
  • cls: 这是约定俗成的名称,代表类本身,你可以用其他名字(如 my_class),但使用 cls 是 Python 社区的标准,能提高代码可读性。
  • arg1, arg2, ...: 除了 cls 之外,方法可以接受其他参数。

@classmethod vs. @staticmethod vs. 实例方法

为了更好地理解 @classmethod,我们通常会将它与另外两种方法进行比较:实例方法静态方法

特性 实例方法 类方法 静态方法
定义方式 def method(self, ...) @classmethod def method(cls, ...) @staticmethod def method(...)
第一个参数 self (类的实例) cls (类本身) 无 (不接收 selfcls)
如何调用 instance.method()Class.method() Class.method()instance.method() Class.method()instance.method()
主要用途 操作或修改实例状态(实例的属性) 操作或修改类状态(类的属性),或作为工厂方法 与类或实例无关的工具函数,逻辑上属于类的一部分
访问权限 可以访问实例属性 (self.attr) 和类属性 (self.Class.attrcls.attr) 可以访问类属性 (cls.attr),不能直接访问实例属性 不能访问实例属性或类属性(除非显式传入)

@classmethod 的主要用途

访问和修改类状态

类属性是所有实例共享的,如果你需要修改这个共享的属性,使用类方法是非常好的方式。

class Student:
    # 这是一个类属性,所有 Student 实例共享
    school_name = "Harvard University"
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade
    @classmethod
    def change_school_name(cls, new_name):
        """修改学校名称"""
        print(f"Changing school name from '{cls.school_name}' to '{new_name}'")
        cls.school_name = new_name
# --- 使用 ---
student1 = Student("Alice", 95)
student2 = Student("Bob", 88)
print(f"Original school name: {Student.school_name}")
# 输出: Original school name: Harvard University
# 通过类方法修改
Student.change_school_name("Yale University")
print(f"Student 1's school: {student1.school_name}")
print(f"Student 2's school: {student2.school_name}")
# 输出:
# Student 1's school: Yale University
# Student 2's school: Yale University
# 也可以通过实例调用,但 'cls' 仍然是类本身
student1.change_school_name("MIT")
print(f"Final school name: {Student.school_name}")
# 输出: Final school name: MIT

作为“工厂方法”(Factory Method)

这是 @classmethod 一个非常强大和优雅的用途,工厂方法用于创建并返回类的实例,特别是当创建过程比较复杂,或者需要根据不同参数返回不同子类实例时。

Python中classmethod与普通方法有何区别?-图2
(图片来源网络,侵删)

场景: 我们有一个 Person 类,但我们希望根据输入的字符串(如 "CN" 或 "US")来创建不同格式的对象。

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    def __repr__(self):
        return f"Person(first_name='{self.first_name}', last_name='{self.last_name}')"
    @classmethod
    def from_string(cls, person_str):
        """
        这是一个工厂方法。
        它接收一个字符串,解析它,并返回一个新的 Person 实例。
        """
        first_name, last_name = person_str.split(" ")
        # 使用 'cls()' 来创建类的实例,而不是硬编码 Person()
        # 这样即使子类继承了这个方法,也能正确创建子类实例
        return cls(first_name, last_name)
# --- 使用 ---
# 通常的创建方式
p1 = Person("John", "Doe")
print(p1)
# 输出: Person(first_name='John', last_name='Doe')
# 使用工厂方法创建
person_data = "Jane Smith"
p2 = Person.from_string(person_data)
print(p2)
# 输出: Person(first_name='Jane', last_name='Smith')

工厂方法的优势:

  • 封装:将复杂的实例化逻辑封装在方法内部,对外部调用者隐藏。
  • 灵活性:可以返回不同类型的对象,这在继承和多态中尤其有用。
# 继承场景下的工厂方法
class USPerson(Person):
    def __repr__(self):
        return f"US Citizen: {self.first_name} {self.last_name}"
# USPerson 类继承了 from_string 类方法
us_person_data = "George Washington"
us_p = USPerson.from_string(us_person_data)
print(us_p)
# 输出: US Citizen: George Washington
# 注意:这里返回的是 USPerson 实例,而不是 Person 实例!
# 这是因为 'cls' 指的是调用该方法的类(USPerson)。

什么时候该用 @classmethod

当你需要以下功能时,就应该使用 @classmethod

  1. 你需要操作类本身的状态(类属性),而不是某个特定实例的状态。
  2. 你需要创建一个“工厂”,用于根据不同的输入或逻辑生成并返回类的实例。
  3. 你希望将一些与类相关联的逻辑组织在类内部,但又不需要访问实例数据,此时比静态方法更合适,因为它隐式地接收了类作为参数。

记住这个简单的规则:如果方法的逻辑依赖于本身,而不是类的实例,那么它就是一个很好的候选者,可以声明为 @classmethod

Python中classmethod与普通方法有何区别?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇