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


当前回答

@classmethod:可用于创建对该类创建的所有实例的共享全局访问。。。。。比如由多个用户更新记录。。。。我特别发现它在创建单件时也很有用。:)

@静态方法:与关联的类或实例无关。。。但为了可读性,可以使用静态方法

其他回答

静态方法是一种对所调用的类或实例一无所知的方法。它只获取传递的参数,而不是隐式的第一个参数。它在Python中基本上是无用的——您可以只使用模块函数而不是静态方法。

另一方面,类方法是一种方法,它将被调用的类或被调用的实例的类作为第一个参数传递。当您希望该方法成为类的工厂时,这很有用:因为它获得了作为第一个参数调用的实际类,所以即使涉及子类,您也可以始终实例化正确的类。例如,观察类方法dict.fromkeys()在子类上调用时如何返回子类的实例:

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

要决定是使用@staticmethod还是@classmethod,必须查看方法内部。如果您的方法访问类中的其他变量/方法,请使用@classmethod。另一方面,如果您的方法不涉及类的任何其他部分,则使用@staticmethod。

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to another class,
        #       you don't have to rename the referenced class 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Making juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing apple %d...' % apple)
        cls._counter += 1

staticmethod无法访问继承层次结构中对象、类或父类的属性。它可以直接在类中调用(无需创建对象)。

classmethod无法访问对象的属性。但是,它可以访问继承层次结构中的类和父类的属性。它可以直接在类中调用(无需创建对象)。如果在对象处调用,则它与不访问self的普通方法相同<属性>并访问self__第__类<属性>。

假设我们有一个b=2的类,我们将创建一个对象,并将其重新设置为b=4。Staticmethod无法访问以前的任何内容。Classmethod只能通过cls.b访问.b==2。正常方法可以通过self访问:.b==4和.b==2__第__.b类。

我们可以遵循KISS风格(保持简单,愚蠢):不要使用静态方法和类方法,不要在没有实例化它们的情况下使用类,只访问对象的属性self.attribute。有些语言是这样实现OOP的,我认为这不是坏主意

一个非常重要的实际差异发生在子类化时。如果你不介意的话,我会劫持@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)

只有第一个参数不同:

normal方法:当前对象作为(附加)第一个参数自动传递classmethod:当前对象的类自动作为(附加的)第一个参数传递staticmethod:不会自动传递额外的参数。传递给函数的就是得到的。

更详细地说。。。

正常方法

“标准”方法,如在所有面向对象的语言中。当调用对象的方法时,会自动为其提供一个额外的参数self作为其第一个参数。即,方法

def f(self, x, y)

必须使用2个参数调用。self是自动传递的,它是对象本身。类似于这个神奇地出现在例如java/c++中,只有在python中才显式显示。

实际上,第一个参数不必称为self,但它是标准的约定,所以请保留它

类方法

装饰方法时

@classmethod
def f(cls, x, y)

自动提供的参数不是self,而是self的类。

静态法

装饰方法时

@staticmethod
def f(x, y)

该方法根本没有给出任何自动参数。它只提供调用它的参数。

用法

classmethod主要用于替代构造函数。staticmethod不使用对象的状态,甚至不使用类本身的结构。它可以是类外部的函数。它只放在类中,用于对具有类似功能的函数进行分组(例如,像Java的Math类静态方法)

class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)