Files
coding-mentor-agent/kb/python-course-kb-practical-python/wiki/concepts/Python-staticmethod-与-classmethod.md
T

7.3 KiB

sources, brief
sources brief
summaries/07_Advanced_Topics__00_Overview.md
summaries/05_Decorated_methods.md
staticmethod 与 classmethod 是用于定义类级方法行为的 Python 内置装饰器。

Python staticmethod 与 classmethod

@staticmethod 与 @classmethod 是 Python 类定义中常用的内置装饰器,用于改变方法与实例、类之间的绑定方式。它们都把函数放在类的命名空间中,但对调用时自动传入的参数有不同规则。

相关来源:summaries/05_Decorated_methods

核心区别

在普通实例方法中,第一个参数通常是 self,表示当前实例:

class Foo:
    def bar(self):
        print(self)

调用实例方法时:

f = Foo()
f.bar()

Python 会自动把实例 f 作为第一个参数传入 bar()。

而 @staticmethod 和 @classmethod 改变了这种默认绑定方式:

class Foo:
    @staticmethod
    def spam(a):
        ...

    @classmethod
    def grok(cls, a):
        ...
  • @staticmethod:不会自动接收实例 self,也不会自动接收类 cls。
  • @classmethod:自动接收类对象作为第一个参数,通常命名为 cls。

相关概念:Python面向对象编程、Python装饰器、self与cls

@staticmethod:静态方法

@staticmethod 用于定义静态方法。静态方法属于类,但不依赖具体实例,也不依赖类对象本身。

示例:

class Foo:
    @staticmethod
    def bar(x):
        print('x =', x)

Foo.bar(2)

输出:

x = 2

这里调用 Foo.bar(2) 时,Python 不会额外传入 self 或 cls,参数 x 就是调用者显式传入的 2。

静态方法适合什么场景

静态方法适合表示“逻辑上属于这个类,但不需要访问实例状态或类状态”的函数。

常见用途包括:

  • 类内部的辅助函数;
  • 与类相关的工具逻辑;
  • 管理实例创建、资源、持久化、锁等支持代码;
  • 某些设计模式中的类级工具函数。

例如,一个类可能需要一些格式化、校验、转换等辅助逻辑。如果这些逻辑不需要访问 self 或 cls,可以定义为静态方法。

@classmethod:类方法

@classmethod 用于定义类方法。类方法调用时会自动接收类对象作为第一个参数,通常命名为 cls。

示例:

class Foo:
    def bar(self):
        print(self)

    @classmethod
    def spam(cls):
        print(cls)

调用:

f = Foo()
f.bar()      # 打印实例 f
Foo.spam()   # 打印类 Foo

普通实例方法中的 self 指向对象实例;类方法中的 cls 指向类本身。

这使得类方法可以访问类对象,并用类对象创建新实例或操作类级状态。

类方法最常见用途:替代构造器

在 summaries/05_Decorated_methods 中,@classmethod 最重要的用途是定义“替代构造器”。

例如,普通构造器可能要求传入年月日:

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

如果希望提供一个“创建今天日期”的构造方式,可以写成类方法:

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def today(cls):
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

调用:

d = Date.today()

这里 today() 不需要调用者手动准备年月日,而是由类方法内部根据当前时间构造对象。

相关概念:concepts/替代构造器、封装

为什么类方法适合继承

类方法的一个重要优点是继承友好。

如果在类方法中使用:

return cls(...)

而不是:

return Date(...)

那么当子类调用该方法时,cls 会自动变成子类。

示例:

class Date:
    @classmethod
    def today(cls):
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

class NewDate(Date):
    pass

d = NewDate.today()

此时 NewDate.today() 中的 cls 是 NewDate,所以返回的是 NewDate 实例,而不是写死的 Date 实例。

这正是 @classmethod 在对象构造中比静态方法或硬编码类名更灵活的地方。

相关概念:Python继承、面向对象设计

实践示例:Portfolio.from_csv()

在 summaries/05_Decorated_methods 的练习中,@classmethod 被用于重构 Portfolio 对象的创建过程。

原来的设计中,读取 CSV、解析数据、创建 Stock 对象、创建 Portfolio 对象的逻辑分散在外部函数中。这会导致责任不清晰。

改进后的设计是让 Portfolio 自己负责从 CSV 创建实例:

class Portfolio:
    def __init__(self):
        self.holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self.holdings.append(holding)

    @classmethod
    def from_csv(cls, lines, **opts):
        self = cls()
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

        for d in portdicts:
            self.append(stock.Stock(**d))

        return self

使用方式:

with open('Data/portfolio.csv') as lines:
    port = Portfolio.from_csv(lines)

这个例子体现了类方法的典型价值:

  • 将对象创建逻辑封装进类;
  • 让外部代码不必知道类的内部构造过程;
  • 保持类型检查和内部一致性;
  • 使用 cls() 支持未来的子类扩展。

相关概念:对象构造、类型检查、封装

选择 staticmethod 还是 classmethod

可以用以下判断标准:

使用 @staticmethod 的情况

当方法满足以下条件时,可以考虑静态方法:

  • 逻辑属于这个类的语义范围;
  • 不需要访问实例属性;
  • 不需要访问类对象;
  • 只是一个放在类中的辅助函数。

使用 @classmethod 的情况

当方法满足以下条件时,应优先考虑类方法:

  • 需要知道当前调用的类;
  • 需要创建当前类或子类的实例;
  • 需要实现替代构造器;
  • 需要与继承机制协同工作;
  • 需要访问或修改类级状态。

对比总结

方法类型 装饰器 自动传入参数 常见用途
实例方法 无 self,当前实例 操作实例状态
静态方法 @staticmethod 无 类相关辅助函数
类方法 @classmethod cls,当前类 替代构造器、继承友好的类级逻辑

关键理解

staticmethod 和 classmethod 都是类定义中的方法组织工具,但它们表达的设计意图不同:

  • @staticmethod 表示“这个函数与类有关,但不需要类或实例参与”;
  • @classmethod 表示“这个方法作用于类本身,并且可能需要根据当前类创建对象”。

在面向对象设计中,@classmethod 尤其适合把对象创建逻辑放回类内部,使代码更清晰、更封装,也更适合继承扩展。

See also: summaries/07_Advanced_Topics__00_Overview