Blog

Python 中 classmethod 和 staticmethod 的区别

该用哪种方法,只取决于方法需要什么:当前实例、类,还是都不需要。选错了,子类会悄无声息地出问题。

方法有三种,怎么选不是风格问题,而是看这个方法实际需要什么。

  • 实例方法需要这个具体的对象。
  • 类方法classmethod)需要类。
  • 静态方法staticmethod)两者都不需要。

一个类里的三种方法

from datetime import date

class Employee:
    raise_pct = 1.05

    def __init__(self, name, salary):
        self.name, self.salary = name, salary

    def give_raise(self):                      # instance method: needs this employee
        self.salary = round(self.salary * self.raise_pct, 2)

    @classmethod
    def from_csv(cls, line):                   # classmethod: needs the class
        name, salary = line.split(',')
        return cls(name, float(salary))

    @staticmethod
    def is_payday(d):                          # staticmethod: needs neither
        return d.day == 25

e = Employee.from_csv('Ada,50000')
e.give_raise()
print(e.name, e.salary)
print(Employee.is_payday(date(2026, 9, 25)), Employee.is_payday(date(2026, 9, 3)))

输出:

Ada 52500.0
True False

from_csv 是类方法的经典用法:备选构造函数。Python 每个类只有一个 __init__,所以其他构建对象的方式都写成类方法。

为什么 from_csvcls

这是关键所在,也是类方法不只是“恰好收到了类的静态方法”的原因。

# why from_csv uses cls and not Employee
class Contractor(Employee):
    raise_pct = 1.10

c = Contractor.from_csv('Grace,60000')
print(type(c).__name__, c.name, c.salary)
c.give_raise()
print('after raise:', c.salary)

输出:

Contractor Grace 60000.0
after raise: 66000.0

Contractor.from_csv 生成的是 Contractor,不是 Employee,加薪也用的是 1.10。没有人在 Contractor 上写过 from_csv——cls 之所以是 Contractor,是因为调用就是在它上面发起的。

把类名写死,这就不灵了:

# hard-coding the class breaks the subclass
class Broken(Employee):
    @classmethod
    def from_csv(cls, line):
        name, salary = line.split(',')
        return Employee(name, float(salary))   # wrong: always the base class

print(type(Broken.from_csv('Alan,70000')).__name__)

输出:

Employee

Broken.from_csv 返回的是 Employee。每个子类都悄悄拿到了错误的类型,而且不会抛任何异常。备选构造函数一律用 cls

各自收到了什么

class Show:
    def instance(self):  return f"got {type(self).__name__} instance"
    @classmethod
    def klass(cls):      return f"got the class {cls.__name__}"
    @staticmethod
    def static():        return "got nothing"

s = Show()
print(s.instance())
print(Show.klass())
print(Show.static())

输出:

got Show instance
got the class Show
got nothing

什么时候用静态方法

很少。老实说:当一个普通函数就够用,但你想把它放进相关类的命名空间时。is_payday 既不碰某个员工,也不碰类——它是一个关于日期的函数。

如果静态方法跟这个类也没什么关系,它就该是模块级函数。放进类里,除了名字变长,什么也得不到。

要点

  • 读取或修改当前对象的,用实例方法。
  • 备选构造函数用类方法,并且用 cls,这样子类才能正常工作。
  • 方法两者都不需要,而且和类放在一起确实有帮助时,才用静态方法。
  • 在备选构造函数里写死类名是个 bug,要等到有人继承你的类时才会暴露。

这篇文章对你有帮助吗?

点一颗爱心来评分!

平均评分 0 / 5. 投票总数: 0

还没有人投票。来做第一个评分的人吧。