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


当前回答

我的贡献展示了@classmethod、@staticmethod和实例方法之间的区别,包括实例如何间接调用@staticmmethod。但是,与其从实例间接调用@staticmethod,不如将其私有化可能更“Python化”。这里没有演示从私有方法获取内容,但基本上是相同的概念。

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

其他回答

您可能需要考虑以下两者之间的区别:

class A:
    def foo():  # no self parameter, no decorator
        pass

and

class B:
    @staticmethod
    def foo():  # no self parameter
        pass

这在python2和python3之间发生了变化:

蟒蛇2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

蟒蛇3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

因此,在python3中,对仅直接从类调用的方法使用@staticmethod已成为可选的。如果要从类和实例调用它们,仍然需要使用@staticmethoddecorator。

其他案例都被未用的答案很好地涵盖了。

顾名思义,类方法用于更改类而不是对象。要对类进行更改,它们将修改类属性(而不是对象属性),因为这就是更新类的方式。这就是类方法将类(通常用“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."

要决定是使用@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

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

Python中@staticmethod和@classmethod之间有什么区别?

您可能已经看到了类似于此伪代码的Python代码,它演示了各种方法类型的签名,并提供了一个文档字符串来解释每种类型:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

普通实例方法

首先,我将解释a_nonormal_instance_method。这就是所谓的“实例方法”。当使用实例方法时,它被用作部分函数(与在源代码中查看时为所有值定义的总函数相反),也就是说,当使用时,第一个参数被预定义为对象的实例及其所有给定属性。它绑定了对象的实例,并且必须从对象的实例调用它。通常,它将访问实例的各种属性。

例如,这是一个字符串的实例:

', '

如果我们使用实例方法join来连接另一个可迭代的字符串,很明显,它是实例的函数,除了是可迭代列表['a','b','c']的函数之外:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

绑定的方法

实例方法可以通过点查找绑定,以便稍后使用。

例如,这将str.join方法绑定到“:”实例:

>>> join_with_colons = ':'.join 

稍后,我们可以将其用作已经绑定了第一个参数的函数。这样,它就像实例上的分部函数一样工作:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

静态方法

静态方法不将实例作为参数。

它非常类似于模块级函数。

但是,模块级函数必须存在于模块中,并且必须专门导入到使用它的其他地方。

然而,如果它附加到对象,它将通过导入和继承方便地跟随对象。

静态方法的一个示例是str.maketrans,它是从Python 3中的字符串模块移动来的。它使转换表适合str.translate使用。从字符串的实例中使用它似乎很愚蠢,如下所示,但从字符串模块导入函数相当笨拙,能够从类中调用它很好,如str.maketrans中所示

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

在python 2中,您必须从越来越无用的字符串模块导入此函数:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Class方法

类方法与实例方法相似,因为它接受隐式的第一个参数,但不是接受实例,而是接受类。为了更好地使用语义,它们通常被用作替代构造函数,它将支持继承。

内置类方法最典型的例子是dict.fromkeys。它被用作dict的另一个构造函数(非常适合当你知道你的键是什么并且想要它们的默认值时)

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

当我们对dict进行子类化时,我们可以使用相同的构造函数来创建子类的实例。

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

请参阅panda源代码以了解其他类似的替代构造函数示例,也可以参阅关于classmethod和staticmethod的Python官方文档。