题 Python中的@staticmethod和@classmethod有什么区别?


装饰的功能有什么区别 @staticmethod 一个装饰着 @classmethod


2675
2017-09-25 21:01


起源


为了清洁起见,静态方法有时最好作为python中的模块级函数。使用模块功能,可以更轻松地导入所需的功能并防止不必要的“。”语法(我在看你Objective-C)。类方法有更多的用途,因为它们可以与多态性结合使用来创建“工厂模式”函数。这是因为类方法将类作为隐式参数接收。 - FistOfFury
tl; dr >>与普通方法相比,静态方法和类方法也可以使用类访问,但与类方法不同,静态方法通过继承是不可变的。 - imsrgadich


答案:


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

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,作为第一个参数隐式传递。

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)

Staticmethod用于将与类有逻辑连接的函数分组到类中。


foo 只是一个功能,但是当你打电话时 a.foo 你不只是得到这个功能, 你得到一个带有对象实例的函数的“部分应用”版本 a 绑定为函数的第一个参数。 foo 期待2个论点,而 a.foo 只需要1个参数。

a 一定会 foo。这就是下面的“绑定”一词的含义:

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

a.class_fooa 不一定是 class_foo而是班级 A 一定会 class_foo

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

在这里,使用静态方法,即使它是一种方法, a.static_foo 回来 一个好的'ole函数,没有参数限制。 static_foo 期待1个参数,和 a.static_foo 也期待一个论点。

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

当然,当你打电话时也会发生同样的事情 static_foo 与班级 A 代替。

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

2320
2017-11-03 19:13



我不明白使用staticmethod的问题是什么。我们可以使用一个简单的外部函数。 - Alcott
@Alcott:您可能希望将函数移动到类中,因为它在逻辑上属于该类。在Python源代码(例如multiprocessing,turtle,dist-packages)中,它用于从模块命名空间“隐藏”单下划线“私有”函数。然而,它的使用高度集中在几个模块中 - 可能表明它主要是一种风格的东西。虽然我找不到任何这样的例子, @staticmethod 可能有助于通过子类覆盖来组织代码。没有它,你就会在模块名称空间中浮动函数的变体。 - unutbu
...以及一些解释 哪里 和 为什么 使用实例,类或静态方法。你没有说一句,但OP也没有问过这个问题。 - MestreLion
@Alcott:正如unutbu所说,静态方法是一种组织/风格特征。有时一个模块有很多类,一些辅助函数在逻辑上与给定的类相关联而不与其他类相关联,因此有意义的是不要用许多“自由函数”“污染”模块,并且最好使用静态方法比依赖于混合类和函数的糟糕风格defs一起代码只是为了表明它们是“相关的” - MestreLion
@unutbu:你应该删除那个Guido引用:它来自python 2.2发行说明的早期草稿,并在2.2.3中更改为: However, class methods are still useful in other places, for example, to program inheritable alternate constructors. 即使是最初的引用也是非常误导的,因为他只说python发行版没有使用 classmethod。但是,仅仅因为python本身不使用给定的功能,它并不意味着它是一个无用的功能。想一想 复数 甚至 开方:python本身也可能不会使用它,但它远没有提供无用 - MestreLion


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

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

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

654
2017-09-25 21:05



静态方法并非无用 - 它是一种将函数放入类中的方法(因为它在逻辑上属于那里),同时表明它不需要访问类。 - Tony Meyer
因此,只是“基本上”没用。这种组织以及依赖注入是static方法的有效用法,但由于模块(而不是Java中的类)是Python中代码组织的基本元素,因此它们的使用和实用性很少。 - Thomas Wouters
在与类或其实例无关的情况下,在类中定义方法有什么逻辑? - Ben James
也许是为了继承缘故?静态方法可以像实例方法和类方法一样继承和覆盖,并且查找按预期工作(与Java不同)。无论是在类还是实例上调用,静态方法都不是静态解析的,因此类和静态方法之间的唯一区别是隐式的第一个参数。 - haridsv
它们还创建了一个更清晰的命名空间,并且更容易理解该函数与该类有关。 - Imbrondir


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


110
2017-09-25 21:07





官方python文档:

@classmethod

类方法接收类为   隐含的第一个参数,就像一个   instance方法接收实例。   要声明一个类方法,请使用它   成语:

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

@classmethod 形式是一种功能    装饰  - 见描述   函数定义 功能   定义 详情。

它可以在课堂上调用   (如 C.f())或在一个实例上   (如 C().f())。实例是   被忽略了除了它的类。如果一个   为派生调用类方法   class,派生类对象是   作为隐含的第一个参数传递。

类方法与C ++不同   或Java静态方法。如果你想   那些,看 staticmethod() 在这   部分。

@staticmethod

静态方法不会收到   隐含第一个参数。宣布一个   静态方法,使用这个成语:

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

@staticmethod 形式是一种功能    装饰  - 见描述   函数定义 功能   定义 详情。

它可以在课堂上调用   (如 C.f())或在一个实例上   (如 C().f())。实例是   被忽略了除了它的类。

Python中的静态方法是类似的   那些在Java或C ++中找到的。为一个   更高级的概念,请参阅    classmethod() 在这个部分。


76
2017-11-03 19:23





这里 是关于这个问题的简短文章

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

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


55
2017-11-03 19:02



那么这是否意味着通过使用static方法我总是绑定到Parent类,并且使用classmethod我绑定了我声明classmethod的类(在这种情况下是子类)? - Mohan Gulati
不。使用静态方法你根本不受约束;没有隐含的第一个参数。通过使用classmethod,您可以获得调用方法的类(如果直接在类上调用它)的隐式第一个参数,或者调用方法的实例的类(如果在实例上调用它)。 - Matt Anderson
可以展开一点来表明,通过将类作为第一个参数,类方法可以直接访问其他类属性和方法,而静态方法则不能(它们需要对MyClass.attr进行硬编码) - MestreLion


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

44
2018-04-22 15:40



可惜你的静态方法实例 不 访问一个类变量。也许你可以改进你的榜样。 - Cilyan


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_normal_instance_method。这恰恰被称为“实例方法“。当使用实例方法时,它被用作部分函数(与在源代码中查看时为所有值定义的总函数相对),即,在使用时,第一个参数被预定义为实例具有所有给定属性的对象,它具有绑定到它的对象的实例,并且必须从对象的实例调用它。通常,它将访问实例的各种属性。

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

', '

如果我们使用实例方法, join 在这个字符串上,加入另一个iterable, 它显然是实例的一个功能,除了是可迭代列表的函数之外, ['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,离开了 string Python 3中的模块。它使转换表适合消费 str.translate。从字符串实例中使用时看起来确实很傻,如下所示,但是从中导入函数 string 模块是相当笨拙的,能够从类中调用它是很好的,就像在 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'

类方法

类方法类似于实例方法,因为它采用隐式的第一个参数,但不是采用实例,而是采用类。通常这些用作替代构造函数以获得更好的语义用法,并且它将支持继承。

内置类方法的最典型示例是 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'>

熊猫源代码 有关替代构造函数的其他类似示例,另请参阅官方Python文档 classmethod 和 staticmethod


38
2018-01-23 20:01





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

例如,如果要创建工厂方法(一个函数根据它获取的参数返回类的不同实现的实例),您可以执行以下操作:

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和静态方法的一个很好的例子, 静态方法显然属于类,因为它在内部使用类Cluster。 classmethod只需要有关类的信息,而不需要对象的实例。

制作的另一个好处 _is_cluster_for 方法一个类方法是这样一个子类可以决定改变它的实现,也许是因为它非常通用并且可以处理多种类型的集群,所以只检查类的名称是不够的。


27
2018-02-24 09:32





我认为一个更好的问题是“你什么时候使用@classmethod vs @staticmethod?”

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

@staticmethod提供了边际性能提升,但我还没有看到一个类中的静态方法的有效使用,这种方法无法作为类外的独立函数实现。


26
2018-05-19 15:27





静态方法:

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

静态方法的好处:

  • 它在classscope中本地化函数名称
  • 它将功能代码移动到更接近使用位置的位置
  • 导入和模块级函数更方便,因为不必专门导入每个方法

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

分类方法:

  • 具有第一个参数作为类名的函数。
  • 可以通过类和实例调用。
  • 这些是使用classmethod内置函数创建的。

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

23
2017-10-03 10:41





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

>>> 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 有一个运行时开销,但可以访问拥有类。或者,我建议使用元类并将类方法放在该元类上:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

19
2017-09-25 21:24



元类有什么问题? - Armin Ronacher
使用元类有什么好处? - Daryl Spitzer
对我来说,一个元类的一个可能的缺点就是你不能直接在一个实例上调用classmethod。 c = C(); c.foo() 引发AttributeError,你必须这样做 type(c).foo()。这也可能被认为是一个功能 - 我想不出你为什么要这么做。 - Aaron Hall♦