@staticmethod和@classmethod有什么区别?

时间:2008-09-25 21:01:58

标签: python oop methods python-decorators

@staticmethod修饰的功能和用@classmethod修饰的功能有什么区别?

33 个答案:

答案 0 :(得分:2743)

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

人们在课堂方法中找到的一种用法是创建inheritable alternative constructors


使用staticmethods self(对象实例)和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_foo a未绑定class_foo,而A类绑定class_foo

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

在这里,使用static方法,即使它是一种方法,a.static_foo也会返回 一个好的'ole函数,没有参数限制。 static_foo期待1个参数,并且 a.static_foo也需要1个参数。

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

当然,当您使用课程static_foo致电A时,会发生同样的事情。

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

答案 1 :(得分:735)

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

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

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

答案 2 :(得分:126)

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

答案 3 :(得分:86)

官方python文档:

@classmethod

  

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

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

@classmethod表单是一个函数   decorator - 请参阅说明   Function definitions中的函数定义以获取详细信息。

     

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

     

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

@staticmethod

  

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

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

@staticmethod表单是一个函数   decorator - 请参阅说明   Function definitions中的函数定义以获取详细信息。

     

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

     

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

答案 4 :(得分:65)

Here是关于此问题的简短文章

  

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

     

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

答案 5 :(得分:54)

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

答案 6 :(得分:47)

  

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

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

', '

如果我们在这个字符串上使用实例方法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,从Python 3中的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}

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

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

答案 7 :(得分:29)

在python 2.4中添加了@decorators如果你正在使用python&lt; 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方法成为类方法的另一个好处是,子类可以决定更改它的实现,可能因为它非常通用并且可以处理多种类型的集群,所以只需检查名称即可。上课是不够的。

答案 8 :(得分:28)

我认为更好的问题是&#34;你什么时候使用@classmethod vs @staticmethod?&#34;

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

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

答案 9 :(得分:26)

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

类方法:与Java和C ++不同,Python没有构造函数重载。因此,要实现这一点,您可以使用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

现在,如果您需要使用单个名称创建一个类,只需first_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")

答案 10 :(得分:26)

静态方法:

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

静态方法的好处:

  • 它在classscope中定位函数名称
  • 它将功能代码移动到更接近使用位置
  • 导入与模块级函数相比更方便,因为不必专门导入每种方法

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

班级方法:

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

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

答案 11 :(得分:21)

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

答案 12 :(得分:19)

The definitive guide on how to use static, class or abstract methods in Python是此主题的一个很好的链接,并将其汇总如下。

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

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

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

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

答案 13 :(得分:13)

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

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

差异: classmethod会将类本身作为第一个参数接收,而staticmethod则不会。

因此,静态方法在某种意义上并不局限于Class本身,只是因为它可能具有相关功能而挂在那里。

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

答案 14 :(得分:10)

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

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

然后你要覆盖子类中的bar()

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

这有效,但请注意,现在子类(bar())中的Foo2实现无法再利用该类的特定内容。例如,假设Foo2有一个名为magic()的方法,您希望在Foo2的{​​{1}}实施中使用该方法:

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" 中拨打Foo2.magic(),但是您自己重复(如果bar()的名称发生变化,则您必须请记住更新Foo2方法。

对我来说,这有点违反open/closed principle,因为bar()中的决定会影响您在派生类中重构公共代码的能力(即它不那么公开扩展)。如果Foobar(),我们就可以了:

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()

答案 15 :(得分:8)

我将尝试使用示例解释基本区别。

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-静态方法不能调用self方法但可以调用其他静态和classmethod

3-静态方法属于类,根本不会使用对象。

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

答案 16 :(得分:5)

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

@static方法:与关联的类或实例无关......但为了便于阅读,可以使用静态方法

答案 17 :(得分:3)

我的贡献展示了@classmethod@staticmethod和实例方法之间的区别,包括实例如何间接调用@staticmethod。但是,与其从实例中间接调用@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)
'''
"""

答案 18 :(得分:3)

当有继承时就会有差异。

假设有两个类——Parent 和 Child。如果要使用@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()

答案 19 :(得分:1)

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

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."

答案 20 :(得分:1)

分析@staticmethod 字面,提供不同的见解。

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

静态方法确实是一个与类定义之外的方法相同的正常函数 幸运的是,它只是为了在应用它的地方站得更近,或者你可以滚动来找到它。

答案 21 :(得分:1)

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

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

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

这在python2和python3之间已经改变:

python2:

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

python3:

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

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

unutbus回答已经很好地覆盖了其他情况。

答案 22 :(得分:1)

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

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

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

答案 23 :(得分:1)

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

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 的名字,其中名字和姓氏用空格分隔。在这种情况下,我们可能会使用名为 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. ...

参考资料

答案 24 :(得分:1)

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

请注意,这并不意味着您不能在staticmethod中使用其他方法,您只需要直接引用该类,这意味着子类的静态方法仍将引用父类:

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)

答案 25 :(得分:1)

实例方法

+ 可以修改对象实例状态

+ 可以修改类状态

类方法

- 不能修改对象实例状态

+ 可以修改类状态

静态方法

- 不能修改对象实例状态

- 不能修改类状态

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

我们实际上可以访问对象instance的实例方法,对,这是我的类对象的实例,而使用class方法,我们可以访问类本身。但不是任何对象,因为类方法实际上并不关心存在的对象。但是,您可以在对象实例上调用类方法和静态方法。这将起作用,但实际上并没有什么不同,因此,再次在这里调用静态方法时,它将起作用,并且将知道您要调用哪个方法。

静态方法用于执行一些实用程序任务,而类方法用于工厂方法。工厂方法可以返回不同用例的类对象。

最后,是一个简短的示例,可以帮助您更好地理解:

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

答案 26 :(得分:0)

静态方法和类方法的定义来自它的文档。以及何时使用静态方法以及何时使用类方法。

  • 静态方法就像java和C#中的静态方法一样,它不会使用类的任何初始化值,它需要从外部正常工作。

  • 类方法:一般用于继承覆盖,当我们覆盖一个方法时,然后我们使用CLS实例来告诉我们是要调用子类还是父类的方法.如果您想使用具有相同名称和不同签名的两种方法。

静态方法(函数)-> 方法

Convert a function to be a static method.

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

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

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. 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 the classmethod builtin.
"""

classmethod(function) -> 方法

Convert a function to be a class method.

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:

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

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. 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 the staticmethod builtin.

答案 27 :(得分:0)

tldr;

staticmethod 本质上是一个绑定到类(及其实例)的函数

classmethod 本质上是一个可继承的 staticmethod

详情请看其他人的优秀回答。

答案 28 :(得分:0)

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):
        print("running quad")
        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> 

答案 29 :(得分:0)

静态方法无法访问继承层次结构中的对象,类或父类的服装。 可以直接在类上调用它(无需创建对象)。

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

认为我们有一个带有b=2的类,我们将创建一个对象并将其重新设置为b=4。 静态方法无法访问以前的任何内容。 Classmethod只能通过.b==2访问cls.b。 普通方法可以同时访问:.b==4通过self.b.b==2通过self.__class__.b访问。

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

答案 30 :(得分:0)

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

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

作为准则,将静态方法用作实用程序,例如将类方法用作factory。或定义一个单例。并使用实例方法对实例的状态和行为进行建模。

希望我很清楚!

答案 31 :(得分:0)

常规方法

调用对象的方法时,会自动为其提供一个额外的参数self作为其第一个参数。也就是说,方法

def f(self, x, y)

必须使用2个参数调用。 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):
        return atan(y, x)


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

angle = Point.angle(3, 2)

答案 32 :(得分:-6)

iPython中对其他相同方法的快速破解表明@staticmethod会产生边际性能增益(以纳秒为单位),但除此之外似乎没有任何功能。此外,在编译期间通过staticmethod()处理方法的额外工作(在运行脚本时执行任何代码之前发生)可能会消除任何性能提升。

为了代码可读性,我要避免使用@staticmethod,除非你的方法用于纳秒数的工作量。