用@staticmethod修饰的方法和用@classmethod修饰的方法有什么区别?


当前回答

我认为一个更好的问题是“你什么时候会使用@classmethod vs@staticmethod?”

@classmethod允许您轻松访问与类定义关联的私有成员。这是一种很好的方法来实现单实例,或者控制所创建对象实例数量的工厂类。

@staticmethod提供了边际性能增益,但我还没有看到静态方法在类内的有效使用,而静态方法不能作为类外的独立函数实现。

其他回答

一个非常重要的实际差异发生在子类化时。如果你不介意的话,我会劫持@unsubu的例子:

class A: 
    def foo(self, x): 
        print("executing foo(%s, %s)" % (self, x)) 
 
    @classmethod
    def class_foo(cls, x): 
        print("executing class_foo(%s, %s)" % (cls, x))
 
    @staticmethod 
    def static_foo(x): 
        print("executing static_foo(%s)" % x)

class B(A):
    pass

在class_foo中,该方法知道它是在哪个类上调用的:

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

在static_foo中,无法确定它是在A还是B上调用的:

A.static_foo(1)
# => executing static_foo(1)
B.static_foo(1)
# => executing static_foo(1)

注意,这并不意味着您不能在静态方法中使用其他方法,您只需直接引用类,这意味着子类的静态方法仍将引用父类:

class A:
    @classmethod
    def class_qux(cls, x):
        print(f"executing class_qux({cls}, {x})")
    
    @classmethod
    def class_bar(cls, x):
        cls.class_qux(x)

    @staticmethod
    def static_bar(x):
        A.class_qux(x)

class B(A):
    pass

A.class_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
B.class_bar(1)
# => executing class_qux(<class '__main__.B'>, 1)
A.static_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
B.static_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)

我将用一个例子来解释基本的区别。

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1-我们可以直接调用静态和类方法,而无需初始化

# A.run_self() #  wrong
A.run_static()
A.run_class()

2-静态方法不能调用self方法,但可以调用其他静态和类方法

3-静态方法属于类,根本不会使用对象。

4-类方法不绑定到对象,而是绑定到类。

基本上,@classmethod生成的方法的第一个参数是从中调用的类(而不是类实例),@staticmethod没有任何隐式参数。

顾名思义,类方法用于更改类而不是对象。要对类进行更改,它们将修改类属性(而不是对象属性),因为这就是更新类的方式。这就是类方法将类(通常用“cls”表示)作为第一个参数的原因。

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

另一方面,静态方法用于执行未绑定到类的功能,即它们不会读取或写入类变量。因此,静态方法不将类作为参数。使用它们是为了使类可以执行与类的目的不直接相关的功能。

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

也许一些示例代码会有所帮助:注意foo、class_foo和static_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)

使用classmethods,对象实例的类作为第一个参数而不是self隐式传递。

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

也可以使用类调用class_foo。事实上,如果你定义了类方法,这可能是因为您打算从类而不是从类实例调用它。A.foo(1)会引发TypeError,但A.class_foo(1)工作正常:

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

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


使用staticmethods,self(对象实例)和cls(类)都不会作为第一个参数隐式传递。它们的行为类似于普通函数,只是您可以从实例或类调用它们:

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

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

静态方法用于将与类有某种逻辑联系的函数分组到该类。


foo只是一个函数,但当你调用.foo时,你不只是得到函数,您将得到函数的“部分应用”版本,其中对象实例a作为函数的第一个参数。foo需要2个参数,而a.foo只需要1个参数。

a绑定到foo。这就是以下术语“约束”的含义:

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

对于.class_foo,a不绑定到class_foo,而类a绑定到class-foo。

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

这里,对于staticmethod,即使它是一个方法,a.static_foo也只返回一个没有参数约束的好的ole函数。static_foo需要1个参数,并且.static_foo也需要1个参数。

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

当然,当用类A调用static_foo时也会发生同样的情况。

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