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


当前回答

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

其他回答

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

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

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

希望我清楚!

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

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

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

我想在前面所有答案的基础上补充以下内容,这不是官方的,但符合标准。

首先,你可以考虑总是给予最少的特权。因此,如果您不需要特定于实例的内容,请将其设置为类方法。如果您不需要特定于类的内容,请将其设置为静态方法。

第二件事是考虑您可以通过创建的方法类型进行通信。静态方法-助手函数意味着在类本身之外使用。类函数-可以在没有实例化的情况下调用,但意味着只能与该类一起使用-否则将是一个静态方法!实例方法-仅用于实例。

这可以帮助您沟通模式以及如何使用代码。

class Foo:
    @classmethod
    def bar(cls, id: int = None):
        query = session.query(
            a.id,
            a.name,
            a.address,
        )

        if id is not None:
            query = query.filter(a.id == id)

        return query

例如,上面提到的——没有理由说明方法栏不能是静态的。然而,通过将其设置为类方法,您可以传达它应该由类本身使用,而不是作为其他地方使用的助手函数!

请记住,以上内容不是官方的,而是我个人的偏好

实例方法:

+可以修改对象实例状态

+可以修改类状态

类方法:

-无法修改对象实例状态

+可以修改类状态

静态方法:

-无法修改对象实例状态

-无法修改类状态

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

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

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-类方法不绑定到对象,而是绑定到类。