Python中@staticmethod和@classmethod的区别是什么?

4645 投票
36 回答
1074557 浏览
提问于 2025-04-11 09:23

装饰器标记的方法@staticmethod和用@classmethod标记的方法有什么区别呢?

36 个回答

195

简单来说,@classmethod 是用来定义一个方法的,这个方法的第一个参数是调用它的类,而不是类的实例。换句话说,它可以直接访问类本身。而 @staticmethod 则没有任何隐含的参数,也就是说,它不需要知道是哪个类在调用它。

955

静态方法是一种方法,它对被调用的类或实例一无所知。它只接收传入的参数,没有隐含的第一个参数。

类方法则不同,它会接收到被调用的类,或者是被调用实例的类,作为第一个参数。这在你想让这个方法成为类的工厂时非常有用:因为它会把实际被调用的类作为第一个参数传入,所以即使涉及到子类,你也总是可以实例化正确的类。例如,dict.fromkeys()这个类方法,当在子类上调用时,会返回子类的实例:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 
3902

也许一些示例代码会更有帮助:注意一下 fooclass_foostatic_foo 的调用方式有什么不同:

class A(object):
    def foo(self, x):
        print(f"executing foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"executing class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"executing static_foo({x})")

a = A()

下面是一个对象实例调用方法的常见方式。对象实例 a 会自动作为第一个参数传入。

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>, 1)

对于类方法(classmethod)来说,传入的第一个参数是对象实例的类,而不是 self

a.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)

你也可以通过类来调用 class_foo。实际上,如果你把某个方法定义为类方法,通常是因为你打算从类中调用它,而不是从类的实例中调用。比如 A.foo(1) 会报错,但 A.class_foo(1) 就可以正常工作:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)

人们发现类方法的一个用途是创建可继承的替代构造函数


对于静态方法(staticmethod)来说,既没有 self(对象实例),也没有 cls(类)作为第一个参数自动传入。它们的行为就像普通函数,只不过你可以从实例或类中调用它们:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

静态方法用于将与类有某种逻辑关联的函数归类到这个类中。


foo 只是一个函数,但当你调用 a.foo 时,你得到的不是单纯的函数,而是一个“部分应用”的版本,其中对象实例 a 被绑定为函数的第一个参数。foo 需要两个参数,而 a.foo 只需要一个参数。

a 被绑定到 foo 上。这就是下面所说的“绑定”:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

a.class_foo 中,a 并没有绑定到 class_foo,而是类 A 被绑定到 class_foo

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

在静态方法中,尽管它是一个方法,a.static_foo 只是返回一个没有任何参数绑定的普通函数。static_foo 需要一个参数,而 a.static_foo 也需要一个参数。

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

当然,当你用类 A 来调用 static_foo 时,情况也是一样的。

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

撰写回答