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


当前回答

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官方文档。

其他回答

静态方法是一种对所调用的类或实例一无所知的方法。它只获取传递的参数,而不是隐式的第一个参数。它在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,提供不同的见解。

类的普通方法是一种隐式动态方法,它将实例作为第一个参数。相反,静态方法不将实例作为第一个参数,因此称为“static”。

静态方法确实是一个与类定义之外的函数相同的正常函数。幸运的是,它被分组到类中,只是为了更接近应用它的位置,或者您可以滚动查找它。

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官方文档。

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

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