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


当前回答

类方法将类作为隐式第一参数接收,就像实例方法接收实例一样。它是一个绑定到类而不是类的对象的方法。它可以访问类的状态,因为它使用指向类而不是对象实例的类参数。它可以修改将应用于类的所有实例的类状态。例如,它可以修改将适用于所有实例的类变量。

另一方面,与类方法或实例方法相比,静态方法不接收隐式第一个参数。并且无法访问或修改类状态。它只属于类,因为从设计的角度来看,这是正确的方法。但就功能而言,在运行时,它并不与类绑定。

作为指导,使用静态方法作为实用程序,使用类方法作为工厂。或者可以定义一个单例。并使用实例方法对实例的状态和行为进行建模。

希望我清楚!

其他回答

关于staticmethod vs classmethod的另一个考虑是继承。假设你有以下课程:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

然后您需要在子类中重写bar():

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

这是可行的,但请注意,现在子类(Foo2)中的bar()实现不能再利用该类特有的任何特性。例如,假设Foo2有一个名为magic()的方法,您希望在Foo2实现bar()时使用该方法:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

这里的解决方法是在bar()中调用Foo2.magic(),但随后您重复自己的操作(如果Foo2的名称发生更改,您必须记住更新bar()方法)。

对我来说,这稍微违反了开放/封闭原则,因为在Foo中做出的决定会影响您重构派生类中的公共代码的能力(即扩展的开放性较低)。如果bar()是一个类方法,我们就可以了:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

给出:In Foo2 MAGIC

此外:历史笔记:Guido Van Rossum(Python的创建者)曾将静态方法称为“意外”:https://mail.python.org/pipermail/python-ideas/2012-May/014969.html

我们都知道静态方法有多有限。(它们基本上是一个意外——回到Python 2.2时代,当我发明新型类和描述符时,我想实现类方法,但一开始我不理解它们,意外地先实现了静态方法。然后,删除它们,只提供类方法已经太晚了。

也:https://mail.python.org/pipermail/python-ideas/2016-July/041189.html

老实说,staticmethod是一个错误——我试图做一些类似Java类方法的事情,但一旦发布,我发现真正需要的是类方法。但要摆脱静态方法为时已晚。

实例方法:

+可以修改对象实例状态

+可以修改类状态

类方法:

-无法修改对象实例状态

+可以修改类状态

静态方法:

-无法修改对象实例状态

-无法修改类状态

class MyClass:
    ''' 
    Instance method has a mandatory first attribute self which represent the instance itself. 
    Instance method must be called by a instantiated instance.
    '''
    def method(self):
        return 'instance method called', self
    
    '''
    Class method has a mandatory first attribute cls which represent the class itself. 
    Class method can be called by an instance or by the class directly. 
    Its most common using scenario is to define a factory method.
    '''
    @classmethod
    def class_method(cls):
        return 'class method called', cls
    
    '''
    Static method doesn’t have any attributes of instances or the class. 
    It also can be called by an instance or by the class directly. 
    Its most common using scenario is to define some helper or utility functions which are closely relative to the class.
    '''
    @staticmethod
    def static_method():
        return 'static method called'


obj = MyClass()
print(obj.method())
print(obj.class_method()) # MyClass.class_method()
print(obj.static_method()) # MyClass.static_method()

输出:

('instance method called', <__main__.MyClass object at 0x100fb3940>)
('class method called', <class '__main__.MyClass'>)
static method called

实例方法实际上可以访问对象实例,所以这是我的类对象的一个实例,而使用类方法,我们可以访问类本身。但不适用于任何对象,因为类方法并不真正关心现有的对象。但是,您可以同时调用对象实例上的类方法和静态方法。这将起作用,但实际上并没有什么不同,所以当你在这里调用静态方法时,它会起作用,它会知道你要调用哪个方法。

静态方法用于执行一些实用程序任务,类方法用于工厂方法。工厂方法可以为不同的用例返回类对象。

最后,举一个简短的例子来更好地理解:

class Student:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_from_string(cls, name_string: str):
        first_name, last_name = name_string.split()
        if Student.validate_name(first_name) and Student.validate_name(last_name):
            return cls(first_name, last_name)
        else:
            print('Invalid Names')

    @staticmethod
    def validate_name(name):
        return len(name) <= 10


stackoverflow_student = Student.get_from_string('Name Surname')
print(stackoverflow_student.first_name) # Name
print(stackoverflow_student.last_name) # Surname

静态方法:

没有自参数的简单函数。处理类属性;而不是实例属性。可以通过类和实例调用。内置函数staticmethod()用于创建它们。

静态方法的优点:

它在类作用域中本地化函数名它将功能代码移动到使用位置附近与模块级函数相比,导入更方便,因为不必专门导入每个方法@静态方法定义some_static_method(*args,**kwds):通过

类方法:

将第一个参数作为类名的函数。可以通过类和实例调用。这些是用内置函数中的classmethod创建的。@分类法定义some_class_method(cls,*args,**kwds):通过

让我先告诉一下用@classmethod修饰的方法和@staticmethod修饰的方法之间的相似性。

相似性:它们都可以在类本身上调用,而不仅仅是类的实例。所以,在某种意义上,这两种方法都是Class的方法。

区别:类方法将接收类本身作为第一个参数,而静态方法不接收。

因此,在某种意义上,静态方法并不绑定到类本身,只是因为它可能具有相关功能而挂在那里。

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

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