热门标签:
Q:

Staticmethod和classmethod的区别

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

原网址
A:

也许一些示例代码会有所帮助:注意fooclass_fooA的调用签名的差异:

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。 事实上,如果你定义的东西是 一个classmethod,这可能是因为你打算从类而不是从类实例调用它。 A.foo(1)会引发一个TypeError,但是A.class_foo(1)工作得很好:

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

人们为类方法找到的一个用途是创建可继承的替代构造函数


使用staticmethodsself(对象实例)和cls(类)都不会隐式传递作为第一个参数。 它们的行为就像普通函数,除了你可以从实例或类中调用它们:

a.static_foo(1)
# executing static_foo(1)

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

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


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'>>

在这里,使用staticmethod,即使它是一个方法,a.static_foo只是返回 一个好的'ole函数,没有参数绑定。 A期望1个参数,并且 a.static_foo也期待1个论点。

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

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

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

所有回答

共 29 条

author avatar

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

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

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

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

author avatar

python官方文档:

@classmethod

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

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

@classmethod表单是一个函数 装饰器-参见 函数中的函数定义 定义了解详情。

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

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

@staticmethod

静态方法不接收 隐式第一个参数。 声明 静态方法,用这个成语:

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

@staticmethod表单是一个函数 装饰器-参见 函数中的函数定义 定义了解详情。

它可以在类上调用 (如C.f())或在实例上 (如C().f())。 实例是 除其类而被忽略。

Python中的静态方法类似 给那些在Java或C++中找到的人. 对于一个 更先进的概念,见 classmethod()在本节中。

author avatar

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

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

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

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

author avatar

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

您可能已经看到了类似这样的伪代码的Python代码,它演示了各种方法类型的签名,并提供了一个docstring来解释每个方法类型:

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来连接另一个可迭代的, 很明显,它是实例的函数,除了是可迭代列表的函数之外,['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,从Python3中的string模块移动。 它使一个翻译表适合由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}

在python2中,您必须从越来越不太有用的字符串模块导入此函数:

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

类方法

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

内建classmethod最规范的例子是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'>

有关替代构造函数的其他类似示例,请参阅pandas源代码,另请参阅classmethodstaticmethod上的官方Python文档。

author avatar

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

类方法:Python不像Java和C++没有构造函数重载。 因此,为了实现这一点,你可以使用classmethod。 下面的例子将解释这一点

让我们考虑我们有一个Person类,它接受两个参数first_namelast_name并创建Person的实例。

class Person(object):

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

现在,如果需要只使用单个名称创建一个类,只需afirst_name,则不能在Python中执行类似的操作。

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

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实现相同的事情,如下所述

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个字符的验证,你可以简单地做到这一点。

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

你可以简单地使用class name

调用
Person.validate_name("Gaurang Shah")
author avatar

只有第一个参数不同

  • 正常方法:当前对象自动作为(附加)第一个参数传递
  • classmethod:当前对象的类自动作为(附加)拳头参数传递
  • staticmethod:不会自动传递额外的参数。 你传递给函数的就是你得到的。

更详细地说。..

正常方法

"标准"方法,就像在每种面向对象语言中一样。 当一个对象的方法被调用时,它会自动给出一个额外的参数self作为它的第一个参数。 即方法

def f(self, x, y)

必须用2个参数调用。 self自动传递,它是对象本身。 类似于eg中神奇出现的this。 java/c++,仅在python中显式显示。

实际上,第一个参数不需要被调用self,但它是标准的约定,所以保持它

类方法

当该方法被装饰

@classmethod
def f(cls, x, y)

自动提供的参数不是self,而是self的类。

静态方法

当该方法被装饰

@staticmethod
def f(x, y)

方法根本没有给出任何自动参数。 它只给出了它被调用的参数。

用途

  • classmethod主要用于替代构造函数。
  • staticmethod不使用对象的状态,甚至不使用类本身的结构。 它可以是类外部的函数。 它只放在类内部,用于对具有类似功能的函数进行分组(例如,像Java的Math类静态方法)
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

author avatar

我认为一个更好的问题是"你什么时候会使用@classmethodvs@staticmethod?"

@classmethod允许您轻松访问与类定义关联的私有成员。 这是一个很好的方式来做单例,或工厂类,控制创建的对象的实例存在的数量。

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

author avatar

静态方法:

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

静态方法的好处:

  • 它在classscope中本地化函数名
  • 它将函数代码移动到使用它的位置
  • 导入与模块级函数相比更方便,因为每个方法都不必特别导入

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

类方法:

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

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

@装饰器是在python2.4中添加的如果您使用的是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和静态方法的一个很好的示例, 静态方法显然属于类,因为它在内部使用类集群。 Classmethod只需要有关类的信息,而不需要对象的实例。

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

author avatar

让我先告诉一个用@classmethod修饰的方法与@staticmethod的相似之处。

相似度:两者都可以在本身上调用,而不仅仅是类的实例。 所以,它们在某种意义上都是类的方法

区别:classmethod将接收类本身作为第一个参数,而staticmethod则没有。

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

>>> 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',)
author avatar

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

>>> 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'>
author avatar

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

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

  • Python不必为object实例化bound-method。
  • 它简化了代码的可读性,并且不依赖于对象本身的状态;

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

  • 工厂方法,用于使用例如某种预处理为类创建实例。
  • 静态方法调用静态方法:如果您将一个静态方法拆分为多个静态方法,则不应该对类名进行硬编码,而是使用类方法
author avatar

关于staticmethod vs classmethod的另一个考虑因素是继承。 假设你有以下类:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

然后你想在一个子类中重写bar()

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

这可以工作,但请注意,现在子类(bar())中的bar()实现不再能够利用该类特定的任何内容。 例如,saybar()有一个名为magic()的方法,您希望在bar()bar()实现中使用:

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()中调用bar(),但是你重复自己(如果bar()的名称发生变化,你必须记住更新bar()方法)。

对我来说,这是对open/closed原则的轻微违反,因为在Foo中做出的决定正在影响您在派生类中重构公共代码的能力(即它对扩展的开放程度较低)。 如果bar()是一个classmethod我们会很好:

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

Also:historical note:Guido Van Rossum(Python的创建者)曾经把staticmethod的称为"意外":https://mail.python.org/pipermail/python-ideas/2012-May/014969.html

我们都知道静态方法有多有限。 (他们基本上是一个意外-早在Python2.2天,当我发明了新的风格的类和描述符,我的意思是实现类方法,但起初我不明白他们,不小心首先实现静态方法。 然后删除它们并只提供类方法为时已晚。

还:https://mail.python.org/pipermail/python-ideas/2016-July/041189.html

老实说,staticmethod是一个错误-我试图做一些类似Java类方法的事情,但是一旦它发布,我发现真正需要的是classmethod。 但摆脱staticmethod为时已晚。

author avatar

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

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-我们可以直接调用static和classmethods而无需初始化

# A.run_self() #  wrong
A.run_static()
A.run_class()

2-Static方法不能调用self方法,但可以调用其他static和classmethod

3-Static方法属于class,根本不会使用object。

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

author avatar

差异发生在有继承时。

假设有两个类-父类和子类。 如果要使用@staticmethod,print_name方法应该写两次,因为类的名称应该写在打印行中。

class Parent:
   _class_name = "Parent"

   @staticmethod
   def print_name():
       print(Parent._class_name)


class Child(Parent):
   _class_name = "Child"

   @staticmethod
   def print_name():
       print(Child._class_name)


Parent.print_name()
Child.print_name()

但是,对于@classmethod,不需要编写两次print_name方法。

class Parent:
    _class_name = "Parent"

    @classmethod
    def print_name(cls):
        print(cls._class_name)


class Child(Parent):
    _class_name = "Child"


Parent.print_name()
Child.print_name()
author avatar

@classmethod:可用于创建对该类创建的所有实例的共享全局访问。.... 像由多个用户更新记录。... 我特别发现它在创建单例时也使用ful。.:)

@static方法:与所关联的类或实例无关。..但是为了可读性可以使用静态方法

author avatar

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

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

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

希望我清楚了!

author avatar

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

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

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

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

毕达哥拉斯:

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

毕达哥拉斯:

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

所以只从类中直接调用的方法使用@staticmethod在python3中变得可选。 如果你想从类和实例调用它们,你仍然需要使用@staticmethod装饰器。

联布总线的答复很好地涵盖了其他情况。

author avatar

我的贡献演示了@classmethod@staticmethod和实例方法之间的区别,包括实例如何间接调用a@staticmethod。 但是,而不是从实例间接调用a@staticmethod,使其私有可能更"pythonic"。"从私有方法中获取某些东西并没有在这里演示,但它基本上是相同的概念。

#!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)
'''
"""
author avatar

实例方法:

+可以修改对象实例状态

+可以修改类状态

类方法:

-无法修改对象实例状态

+可以修改类状态

静态方法:

-无法修改对象实例状态

-无法修改类状态

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
author avatar

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

Analyze@staticmethod字面上提供不同的见解。

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

staticmethod确实是这样一个正常的函数,与类定义之外的函数相同。
幸运的是,它被分组到类中,只是为了站得更近,或者你可以滚动找到它。

author avatar

我认为给出一个纯Python版本的staticmethodclassmethod将有助于理解它们在语言层面的区别。

它们都是非数据描述符(如果您首先熟悉描述符,则更容易理解它们)。

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

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

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner
author avatar

Python带有几个内置的装饰器。 三巨头是:

@classmethod
@staticmethod
@property

@classmethod装饰器可以使用类的实例或直接由类本身作为其第一个参数调用。

@staticmethod是一种将函数放入类中的方法(因为它在逻辑上属于那里),同时表示它不需要访问类。

让我们考虑下面的类:

class DecoratorTest(object):

    def __init__(self):
        pass

    def doubler(self, x):
        print("running doubler")
        return x*2

    @classmethod
    def class_doubler(klass, x):
        print("running doubler: %s" % klass)
        return x*2

    @staticmethod
    def static_doubler(x): # no need adding 'self' here
        print("running doubler")
        return x*2

decor = DecoratorTest()

让我们看看它是如何工作的:

print(decor.doubler(5))
# running doubler
# 10

print(decor.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10
print(DecoratorTest.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10

print(DecoratorTest.static_doubler(5))
# running doubler 
# 10
print(decor.static_doubler(5))
# running doubler 
# 10

print(decor.doubler)
# <bound method DecoratorTest.doubler of <__main__.DecoratorTest object at 0x7f90e74fd150>> 
print(decor.class_doubler)
# <bound method DecoratorTest.class_doubler of <class '__main__.DecoratorTest'>> 
print(decor.static_doubler)
# <function DecoratorTest.static_doubler at 0x7f90e7447440> 
author avatar

子类化时会出现一个非常重要的实际差异。 如果你不介意,我会劫持@unutbu的例子:

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)

请注意,这并不意味着你不能在astaticmethod中使用其他方法,你只需要直接引用该类,这意味着子类的staticmethods仍然会引用父类:

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)
author avatar

首先,让我们从一个示例代码开始,我们将使用它来理解这两个概念:

class Employee:

    NO_OF_EMPLOYEES = 0
  
    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary
        self.increment_employees()

    def give_raise(self, amount):
        self.salary += amount

    @classmethod
    def employee_from_full_name(cls, full_name, salary):
        split_name = full_name.split(' ')
        first_name = split_name[0]
        last_name = split_name[1]
        return cls(first_name, last_name, salary)

    @classmethod
    def increment_employees(cls):
        cls.NO_OF_EMPLOYEES += 1

    @staticmethod
    def get_employee_legal_obligations_txt():
        legal_obligations = """
        1. An employee must complete 8 hours per working day
        2. ...
        """
        return legal_obligations

类方法

类方法接受类本身作为隐式参数和-可选-定义中指定的任何其他参数。 重要的是要理解类方法不能访问对象实例(就像实例方法一样)。 因此,类方法不能用于改变实例化对象的状态,而是能够改变在该类的所有实例之间共享的类状态。 当我们需要访问类本身时,类方法通常很有用-例如,当我们想要创建工厂方法时,即创建类实例的方法。 换句话说,类方法可以作为替代构造函数。

在我们的示例代码中,可以通过提供三个参数来构造Employee的实例;first_namelast_namesalary

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.first_name)
print(employee_1.salary)

'Andrew'
85000

现在让我们假设员工的姓名可能会在一个字段中提供,其中名字和姓氏之间用空格分隔。 在这种情况下,我们可以使用名为employee_from_full_name的类方法,它总共接受三个参数。 第一个是类本身,这是一个隐式参数,这意味着在调用方法时不会提供它—Python会自动为我们执行此操作:

employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(employee_2.first_name)
print(employee_2.salary)

'John'
95000

请注意,也可以从对象实例调用employee_from_full_name,尽管在这种情况下它没有多大意义:

employee_1 = Employee('Andrew', 'Brown', 85000)
employee_2 = employee_1.employee_from_full_name('John Black', 95000)

我们可能想要创建类方法的另一个原因是当我们需要更改类的状态时。 在我们的示例中,类变量NO_OF_EMPLOYEES跟踪当前为公司工作的员工数量。 每次创建Employee的新实例时都会调用此方法,并相应地更新计数:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')
employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')

Number of employees: 1
Number of employees: 2

静态方法

另一方面,在静态方法中,实例(即self)和类本身(即cls)都不是作为隐式参数传递的。 这意味着这些方法不能访问类本身或其实例。 现在有人可能会争辩说,静态方法在类的上下文中并不有用,因为它们也可以放在帮助器模块中,而不是将它们添加为类的成员。 在面向对象编程中,将类结构化为逻辑块非常重要,因此,当我们需要在类下添加方法时,静态方法非常有用,因为它在逻辑上属于类。 在我们的示例中,名为get_employee_legal_obligations_txt的静态方法只返回一个字符串,其中包含公司每个员工的法律义务。 此函数不与类本身交互,也不与任何实例交互。 它可能被放置到不同的帮助器模块中,但是,它只与这个类相关,因此我们必须将它放在Employee类下。

静态方法可以直接从类本身访问

print(Employee.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

或者从类的实例:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

参考文献

author avatar

staticmethod无权访问继承层次结构中对象、类或父类的attibutes。 它可以直接在类中调用(不创建对象)。

classmethod无法访问对象的属性。 但是,它可以访问继承层次结构中类和父类的属性。 它可以直接在类中调用(不创建对象)。 如果在对象处调用,那么它与不访问self.<attribute(s)>而只访问self.__class__.<attribute(s)>的普通方法相同。

假设我们有一个带有b=2的类,我们将创建一个对象,并在其中将其重新设置为b=4。 Staticmethod无法访问以前的任何内容。 Classmethod只能通过cls.b访问cls.b。 普通方法可以访问两者:.b==4viaself.bcls.bviaself.__class__.b

我们可以遵循KISS风格(保持简单,愚蠢):不要使用staticmethods和classmethods,不要在没有实例化它们的情况下使用类,只访问对象的属性self.attribute(s)。 有些语言以这种方式实现OOP,我认为这是个不错的主意。 :)

相似问题