python:@staticmethod和@classmethod的区别是什么?

@staticmethod装饰的函数和用@classmethod装饰的函数有什么区别?


也许一些示例代码会有所帮助:注意fooclass_foostatic_foo调用签名的区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
class A(object):
    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    

a = A()

下面是对象实例调用方法的通常方式。对象实例a作为第一个参数隐式传递。

1
2
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

使用classmethods,对象实例的类被隐式地传递为第一个参数,而不是self

1
2
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

您还可以使用该类调用class_foo。事实上,如果你定义某物为类方法,这可能是因为您打算从类而不是类实例中调用它。A.foo(1)会引发一个类型错误,但A.class_foo(1)工作得很好:

1
2
A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

人们发现类方法的一个用途是创建可继承的替代构造函数。

对于静态方法,既不会隐式地传递self(对象实例)也不会传递cls(类)作为第一个参数。它们的行为像普通函数,除了你可以从一个实例或类调用它们:

1
2
3
4
5
a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

静态方法用于将与类具有某种逻辑连接的函数分组到类。

foo只是一个函数,但是当你调用a.foo时,你不只是得到这个函数,您将得到函数的"部分应用"版本,对象实例a绑定为函数的第一个参数。foo期望2个参数,而a.foo只期望1个参数。

a绑定到foo。这就是下文"界限"一词的含义:

1
2
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

对于a.class_fooa并不绑定到class_foo,而是类A绑定到class_foo

1
2
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

在这里,使用staticmethod,即使它是一个方法,a.static_foo也只返回一个很好的'ole函数,没有参数限制。static_foo需要一个参数,并且a.static_foo也需要一个参数。

1
2
print(a.static_foo)
# <function static_foo at 0xb7d479cc>

当然,当您使用类A调用static_foo时,也会发生同样的事情。

1
2
print(A.static_foo)
# <function static_foo at 0xb7d479cc>


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

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

1
2
3
4
5
6
7
8
9
>>> class DictSubclass(dict):
...     def __repr__(self):
...         return"DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>


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


官方python文档:

@classmethod

A class method receives the class as
implicit first argument, just like an
instance method receives the instance.
To declare a class method, use this
idiom:

1
2
3
class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ...

The @classmethod form is a function
decorator – see the description of
function definitions in Function
definitions for details.

It can be called either on the class
(such as C.f()) or on an instance
(such as C().f()). The instance is
ignored except for its class. If a
class method is called for a derived
class, the derived class object is
passed as the implied first argument.

Class methods are different than C++
or Java static methods. If you want
those, see staticmethod() in this
section.

@staticmethod

A static method does not receive an
implicit first argument. To declare a
static method, use this idiom:

1
2
3
class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

The @staticmethod form is a function
decorator – see the description of
function definitions in Function
definitions for details.

It can be called either on the class
(such as C.f()) or on an instance
(such as C().f()). The instance is
ignored except for its class.

Static methods in Python are similar
to those found in Java or C++. For a
more advanced concept, see
classmethod() in this section.


这里有一篇关于这个问题的短文

@staticmethod function is nothing more than a function defined inside a class. It is callable without instantiating the class first. It’s definition is immutable via inheritance.

@classmethod function also callable without instantiating the class, but its definition follows Sub class, not Parent class, via inheritance. That’s because the first argument for @classmethod function must always be cls (class).


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 other class,
        #       you don't have to rename the class reference

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

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


What is the difference between @staticmethod and @classmethod in Python?

你可能见过这样的Python代码伪代码,它演示了各种方法类型的签名,并提供了一个文档字符串来解释每种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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_normal_instance_method。这就是所谓的"实例方法"。当使用一个实例方法时,它被用作局部函数(而不是一个总函数,定义为所有值在查看源代码),使用时,第一个参数是预先定义的实例对象,与它的所有属性。它绑定了对象的实例,并且必须从对象的实例中调用它。通常,它将访问实例的各种属性。

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

1
', '

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

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

绑定方法

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

例如,它将str.join方法绑定到':'实例:

1
>>> join_with_colons = ':'.join

之后我们可以用这个函数它已经有第一个参数绑定。这样,它就像一个局部函数作用于实例:

1
2
3
4
>>> 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中的string模块中移来的。它使翻译表适合str.translate使用。从字符串的实例中使用它确实看起来相当愚蠢,如下所示,但是从string模块导入函数相当笨拙,能够从类中调用它很好,就像在str.maketrans中一样

1
2
3
4
5
# 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}

在python2中,你必须从越来越没用的string模块中导入这个函数:

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

类方法

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

内建类方法最典型的例子是dict.fromkeys。它用作dict的另一种构造函数(非常适合于您知道您的键是什么并希望为它们设置一个默认值的时候)。

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

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

1
2
3
4
5
6
>>> 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源代码,并请参阅关于classmethodstaticmethod的官方Python文档。


@decorator是在python 2.4中添加的,如果您使用的是python < 2.4,则可以使用classmethod()和staticmethod()函数。

例如,如果你想创建一个工厂方法(一个函数返回一个类的不同实现的实例,这取决于它得到的参数),你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Cluster(object):

    def _is_cluster_for(cls, name):
       """
        see if this class is the cluster with this name
        this is a classmethod
       """

        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
       """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
       """

        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

还要注意,这是一个使用类方法和静态方法的好例子,静态方法显然属于类,因为它在内部使用类集群。classmethod只需要关于类的信息,而不需要对象的实例。

_is_cluster_for方法成为类方法的另一个好处是子类可以决定更改它的实现,这可能是因为它非常通用,可以处理不止一种类型的集群,所以仅仅检查类的名称是不够的。


我认为更好的问题是"什么时候使用@classmethod和@staticmethod?"

@classmethod允许您轻松访问与类定义关联的私有成员。这是实现单例或控制已创建对象实例数量的工厂类的好方法。

@staticmethod提供了有限的性能提升,但是我还没有看到在类中有效地使用静态方法,而不能作为类之外的独立函数来实现。


静态方法:

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

静态方法的好处:

它在类范围内本地化函数名它将函数代码移动到更靠近使用它的地方

与模块级函数相比,导入函数更方便,因为不必特别导入每个方法

1
2
3
@staticmethod
def some_static_method(*args, **kwds):
    pass

类方法:

第一个参数为classname的函数。可以通过类和实例调用。

这些是用classmethod内建函数创建的。

1
2
3
 @classmethod
 def some_class_method(cls, *args, **kwds):
     pass

@staticmethod只是将默认函数禁用为方法描述符。classmethod将函数封装在一个可调用的容器中,该容器将引用作为第一个参数传递给所属类:

1
2
3
4
5
6
7
8
9
10
>>> class C(object):
...  pass
...
>>> def f():
...  pass
...
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

事实上,classmethod有一个运行时开销,但是可以访问拥有它的类。或者,我建议使用一个元类,并把类方法放在这个元类上:

1
2
3
4
5
6
7
8
9
>>> class CMeta(type):
...  def foo(cls):
...   print cls
...
>>> class C(object):
...  __metaclass__ = CMeta
...
>>> C.foo()
<class '__main__.C'>


我开始用c++学习编程语言,然后是Java和Python,所以这个问题也困扰着我,直到我理解了它们的简单用法。

类方法:Python不同于Java和c++,它没有构造函数重载。为此,可以使用classmethod。下面的示例将解释这一点

假设我们有一个Person类,它接受两个参数first_namelast_name并创建Person实例。

1
2
3
4
5
class Person(object):

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

现在,如果需要创建一个只使用一个名称的类,仅仅使用一个first_name,那么在python中就不能这样做。

当您试图创建一个对象(实例)时,这将给您一个错误。

1
2
3
4
5
6
7
8
class Person(object):

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

    def __init__(self, first_name):
        self.first_name = first_name

但是,您可以使用下面提到的@classmethod来实现相同的功能

1
2
3
4
5
6
7
8
9
class Person(object):

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

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name,"")

静态方法::这相当简单,它不绑定到实例或类,您可以简单地使用类名调用它。

因此,让我们在上面的例子中说,您需要验证first_name不应该超过20个字符,您可以简单地这样做。

1
2
3
@staticmethod  
def validate_name(name):
    return len(name) <= 20

你可以简单地用类名调用

1
Person.validate_name("Gaurang Shah")


关于如何在Python中使用静态、类或抽象方法的权威指南是本主题的一个很好的链接,并将其总结如下。

@staticmethod函数只不过是在类中定义的函数。它是可调用的,无需首先实例化类。它的定义通过继承是不可变的。

Python不必为对象实例化绑定方法。它简化了代码的可读性,并且不依赖于对象本身的状态;

@classmethod函数也可以在不实例化类的情况下调用,但是它的定义遵循子类,而不是父类,可以通过继承被子类覆盖。这是因为@classmethod函数的第一个参数必须始终是cls (class)。

工厂方法,用于为类创建实例,例如使用某种预处理。调用静态方法的静态方法:如果将静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法


让我先说明用@classmethod修饰的方法和用@staticmethod修饰的方法之间的相似性。

相似性:它们都可以在类本身上调用,而不仅仅是类的实例。它们都是类的方法。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> 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',)


关于静态方法vs . classmethod的另一个考虑是继承。假设你有以下课程:

1
2
3
4
class Foo(object):
    @staticmethod
    def bar():
        return"In Foo"

然后想要在子类中覆盖bar():

1
2
3
4
class Foo2(Foo):
    @staticmethod
    def bar():
        return"In Foo2"

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

1
2
3
4
5
6
7
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()classmethod,我们就没问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
2
3
# A.run_self() #  wrong
A.run_static()
A.run_class()

静态方法不能调用self方法,但可以调用其他静态方法和类方法

静态方法属于类,不使用对象。

类方法不绑定到对象,而是绑定到类。


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

@static方法:与与…但对于可读性可以使用静态方法


我的贡献展示了@classmethod@staticmethod和实例方法之间的差异,包括实例如何间接调用@staticmethod。但是,与其从实例中间接调用@staticmethod,将它设置为私有可能更加"python化"。这里没有演示从私有方法中获取一些东西,但它基本上是相同的概念。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!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() + '
'
)
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '
'
)
''' 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() + '
'
)
''' 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() + '
'
)
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '
'
)
'''  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() + '
')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

你可能要考虑以下的区别:

1
2
3
Class A:
    def foo():  # no self parameter, no decorator
        pass

1
2
3
4
Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

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

python2:

1
2
3
4
5
6
>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

1
2
3
4
5
>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

因此,在python3中,只对直接从类调用的方法使用@staticmethod是可选的。如果您想同时从类和实例调用它们,您仍然需要使用@staticmethod装饰器。

unutbus answer已经很好地涵盖了其他情况。


类方法,顾名思义,用于修改类而不是对象。要对类进行更改,它们将修改类属性(而不是对象属性),因为这是更新类的方式。这就是类方法将类(通常用"cls"表示)作为第一个参数的原因。

1
2
3
4
5
6
class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print"m is %d" % cls.m

另一方面,静态方法用于执行不绑定到类的功能,即它们不会读取或写入类变量。因此,静态方法不接受类作为参数。它们的使用使得类可以执行与类的用途没有直接关系的功能。

1
2
3
4
5
6
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提供了不同的见解。

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

静态方法实际上是一个与类定义之外的函数相同的普通函数。幸运的是,它被分组到类中,只是为了站得离应用程序更近一些,或者您可以滚动找到它。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/python
#coding:utf-8

class Demo(object):
    def __init__(self,x):
        self.x = x

    @classmethod
    def addone(self, x):
        return x+1

    @staticmethod
    def addtwo(x):
        return x+2

    def addthree(self, x):
        return x+3

def main():
    print Demo.addone(2)
    print Demo.addtwo(2)

    #print Demo.addthree(2) #Error
    demo = Demo(2)
    print demo.addthree(2)


if __name__ == '__main__':
    main()


在iPython中对其他相同的方法进行快速整理后发现,@staticmethod产生了微小的性能提升(以纳秒为单位),但除此之外,它似乎没有任何作用。此外,在编译期间通过staticmethod()处理方法的额外工作(当运行脚本时,这发生在任何代码执行之前)可能会抵消任何性能收益。

为了代码的可读性,我将避免@staticmethod,除非您的方法将用于计算纳秒的负载工作。