@静态方法和@类方法之间的区别

#0装饰的函数和用#1装饰的函数有什么区别?

1009571 次浏览

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

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

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

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

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

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

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

@静态方法函数只不过是在类中定义的函数。无需先实例化类即可调用。它的定义通过继承是不可变的。

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

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

class A(object):def foo(self, x):print(f"executing foo({self}, {x})")
@classmethoddef class_foo(cls, x):print(f"executing class_foo({cls}, {x})")
@staticmethoddef static_foo(x):print(f"executing static_foo({x})")
a = A()

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

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

使用类方法,对象实例的类作为第一个参数隐式传递,而不是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)

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


使用静态方法self(对象实例)和cls(类)都不会隐式传递为第一个参数。它们的行为像普通函数,只是你可以从实例或类调用它们:

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。这就是下面术语“绑定”的含义:

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也需要1个参数。

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

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

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

官方python文档:

类方法接收类作为隐式第一个论点,就像一个实例方法接收实例。要声明类方法,请使用成语:

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

@classmethod形式是一个函数装饰器-查看描述函数定义在函数定义了解详情。

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

类方法不同于C++或者Java静态方法这些,看这里的#0部分。

@静态方法

静态方法不会收到隐式第一个参数。声明一个静态方法,使用这个习语:

class C:@staticmethoddef f(arg1, arg2, ...): ...

@staticmethod形式是一个函数装饰器-查看描述函数定义在函数定义了解详情。

它可以在类中调用(例如C.f())或在实例上(例如C().f())。实例是除了它的类之外被忽略。

Python中的静态方法类似在Java或C++发现的更先进的概念,见#0在本节中。

@装饰器是在python 2.4中添加的如果你使用的是python<2.4,你可以使用类方法()和静态方法()函数。

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

class Cluster(object):
def _is_cluster_for(cls, name):"""see if this class is the cluster with this namethis is a classmethod"""return cls.__name__ == name_is_cluster_for = classmethod(_is_cluster_for)
#static methoddef getCluster(name):"""static factory method, should be in Cluster classreturns a cluster object for the given name"""for cls in Cluster.__subclasses__():if cls._is_cluster_for(name):return cls()getCluster = staticmethod(getCluster)

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

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

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

为了代码的易读性,我将避免@staticmethod,除非您的方法将用于大量工作,其中纳秒计数。

在Python中,@static方法和@class方法有什么区别?

您可能已经看到过类似于此伪代码的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 itsattributes, and other arguments such as arg_1 and kwarg2'''
@staticmethoddef a_static_method(arg_0):'''Return a value that is a function of arg_0. It does not know theinstance or class it is called from.'''
@classmethoddef 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

稍后我们可以将this用作已经绑定了第一个参数的函数。这样,它就像实例上的部分函数一样工作:

>>> 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中,您必须从越来越少用的string模块导入此函数:

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

类方法

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

内建类方法的最规范的例子是dict.fromkeys。它被用作字典的替代构造函数,(非常适合当你知道你的键是什么并想要它们的默认值时。)

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

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

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

有关替代构造函数的其他类似示例,请参阅熊猫源代码,另请参阅#0#1上的官方Python留档。

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

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

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

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

#0函数只不过是在类中定义的函数。无需先实例化类即可调用。它的定义通过继承是不可变的。

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

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

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

要决定是使用@陈志立还是@吴兴龙,您必须查看您的方法内部。如果你的方法访问你的类中的其他变量/方法,那么请使用@类方法。另一方面,如果您的方法没有触及类的任何其他部分,那么请使用@static方法。

class Apple:
_counter = 0
@staticmethoddef 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
@classmethoddef make_apple_juice(cls, number_of_apples):print('Making juice:')for i in range(number_of_apples):cls._juice_this(i)
@classmethoddef _juice_this(cls, apple):print('Juicing apple %d...' % apple)cls._counter += 1

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

class A(object):x = 0
def say_hi(self):pass
@staticmethoddef say_hi_static():pass
@classmethoddef say_hi_class(cls):pass
def run_self(self):self.x += 1print self.x # outputs 1self.say_hi()self.say_hi_static()self.say_hi_class()
@staticmethoddef run_static():print A.x  # outputs 0# A.say_hi() #  wrongA.say_hi_static()A.say_hi_class()
@classmethoddef run_class(cls):print cls.x # outputs 0# cls.say_hi() #  wrongcls.say_hi_static()cls.say_hi_class()

1-我们可以直接调用静态和类方法而无需初始化

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

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

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

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

关于静态方法与类方法的另一个考虑因素是继承。假设你有以下类:

class Foo(object):@staticmethoddef bar():return "In Foo"

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

class Foo2(Foo):@staticmethoddef bar():return "In Foo2"

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

class Foo2(Foo):@staticmethoddef bar():return "In Foo2"@staticmethoddef magic():return "Something useful you'd like to use in bar, but now can't"

这里的解决方法是在bar()中调用Foo2.magic(),但随后您会重复自己(如果Foo2的名称发生变化,您必须记住更新bar()方法)。

对我来说,这稍微违反了开/闭原理,因为Foo中做出的决定影响了你在派生类中重构公共代码的能力(即它对扩展的开放程度较低)。如果bar()classmethod,我们会没事的:

class Foo(object):@classmethoddef bar(cls):return "In Foo"
class Foo2(Foo):@classmethoddef bar(cls):return "In Foo2 " + cls.magic()@classmethoddef magic(cls):return "MAGIC"
print Foo2().bar()

提供:In Foo2 MAGIC

另外:历史笔记:Guido Van Rossum(Python的创建者)曾经将staticmethod称为“意外”:https://mail.python.org/pipermail/python-ideas/2012-May/014969.html

我们都知道静态方法有多有限。(它们基本上是一个意外——回到Python 2.2时代,当我发明新式类和描述符时,我打算实现类方法,但一开始我不理解它们,不小心先实现了静态方法。然后删除它们并只提供类方法为时已晚。

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

老实说,静态方法是一个错误-我试图做一些类似于Java类方法的事情,但一旦它被发布,我发现真正需要的是类方法。

静态方法:

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

静态方法的好处:

  • 它在类作用域中本地化函数名称
  • 它使函数代码更接近使用它的地方
  • 与模块级函数相比,导入更方便,因为每个方法都不必特别导入

    @staticmethoddef some_static_method(*args, **kwds):pass

Class Methods:

  • Functions that have first argument as classname.
  • Can be called through both class and instance.
  • These are created with classmethod in-built function.

     @classmethoddef some_class_method(cls, *args, **kwds):pass

@类方法:可用于创建对该类创建的所有实例的共享全局访问……就像由多个用户更新记录一样……我特别发现它在创建单例时也很有用…:)

@静态方法:与关联的类或实例无关……但为了易读性可以使用静态方法

我从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_nameself.last_name = last_name

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

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

class Person(object):
def __init__(self, first_name, last_name):self.first_name = first_nameself.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_nameself.last_name = last_name
@classmethoddef get_person(cls, first_name):return cls(first_name, "")

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

因此,假设在上面的示例中,您需要验证first_name不应超过20个字符,您可以简单地执行此操作。

@staticmethoddef validate_name(name):return len(name) <= 20

您可以简单地使用class name调用

Person.validate_name("Gaurang Shah")

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

class A(object):m=54
@classmethoddef class_method(cls):print "m is %d" % cls.m

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

class X(object):m=54 #will not be referenced
@staticmethoddef static_method():print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

分析@静态方法字面上提供不同的见解。

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

静态方法确实是一个与类定义之外的函数相同的普通函数。
幸运的是,它被分组到类中,只是为了更接近它被应用的地方,或者你可能会滚动找到它。

首先,让我告诉你一个用@类方法修饰的方法与@static方法之间的相似性。

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

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

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

>>> class Klaus:@classmethoddef classmthd(*args):return args
@staticmethoddef 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',)

我的贡献展示了@classmethod@staticmethod和实例方法之间的区别,包括实例如何间接调用@staticmethod。但是与其从实例间接调用@staticmethod,不如使其私有可能更“pythonic”。

#!python3
from os import systemsystem('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_methodreturn 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'@classmethoddef 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@staticmethoddef static_method():return 'called from inside the static_method()'# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %
# works even if the class hasn't been instantiatedprint(DemoClass.class_method() + '\n')''' called from inside the class_method() '''
# works even if the class hasn't been instantiatedprint(DemoClass.static_method() + '\n')''' called from inside the static_method() '''# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %
# >>>>> all methods types can be called on a class instance <<<<<# instantiate the classdemoclassObj = 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 notprint(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 workprint(DemoClass.instance_method_1() + '\n')'''TypeError: TypeError: unbound method instancemethod() must be called withDemoClass instance as first argument (got nothing instead)'''"""

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

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

class B:@staticmethoddef foo():  # no self parameterpass

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

python2:

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

python3:

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

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

联科行动的答复已充分说明了其他情况。

只有第一个论点不同

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

更详细地…

正常方法

“标准”方法,与所有面向对象语言一样。当调用对象的方法时,它会自动获得一个额外的参数self作为其第一个参数。即方法

def f(self, x, y)

self是自动传递的,它是物体本身。类似于在例如java/c++中神奇地出现的this,只有在python中才会显式显示。

实际上,第一个参数必须不能称为self,但它是标准约定,所以保留它

类方法

当方法被装饰

@classmethoddef f(cls, x, y)

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

静态方法

当方法被装饰

@staticmethoddef f(x, y)

方法没有给出完全没有任何自动参数。它只给出了调用它的参数。

用法

  • classmethod主要用于替代构造函数。
  • staticmethod不使用对象的状态,甚至不使用类本身的结构。它可以是类外部的函数。它只放在类内部,以便对具有类似功能的函数进行分组(例如,像Java的Math类静态方法)
class Pointdef __init__(self, x, y):self.x = xself.y = y
@classmethoddef frompolar(cls, radius, angle):"""The `cls` argument is the `Point` class itself"""return cls(radius * cos(angle), radius * sin(angle))
@staticmethoddef angle(x, y):"""this could be outside the class, but we put it herejust 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)

我认为提供staticmethodclassmethod的纯Python版本将有助于理解它们在语言级别上的区别(参考描述Howto指南)。

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

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

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

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

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

希望我说清楚了!

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

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

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

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

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

class A:def foo(self, x):print("executing foo(%s, %s)" % (self, x)) 
@classmethoddef class_foo(cls, x):print("executing class_foo(%s, %s)" % (cls, x)) 
@staticmethoddef 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:@classmethoddef class_qux(cls, x):print(f"executing class_qux({cls}, {x})")    
@classmethoddef class_bar(cls, x):cls.class_qux(x)
@staticmethoddef 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)

实例方法

+可以修改对象实例状态

+可以修改类状态

类方法

-不能修改对象实例状态

+可以修改类状态

静态方法

-不能修改对象实例状态

-不能修改类状态

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.'''@classmethoddef 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.'''@staticmethoddef 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_nameself.last_name = last_name
@classmethoddef 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')
@staticmethoddef validate_name(name):return len(name) <= 10

stackoverflow_student = Student.get_from_string('Name Surname')print(stackoverflow_student.first_name) # Nameprint(stackoverflow_student.last_name) # Surname

Python带有几个内置装饰器。三大是:

@classmethod@staticmethod@property

首先让我们注意一个类的任何函数都可以用这个类的实例调用(在我们初始化这个类之后)。

@吴兴龙不仅将调用函数作为类的实例,而且将直接由班级本身作为其第一个参数。

@陈志立是将函数放入类中的一种方式(因为它在逻辑上属于那里),同时表明它不需要访问类(所以我们在函数定义中不需要使用#0)。

让我们考虑以下类:

class DecoratorTest(object):
def __init__(self):pass
def doubler(self, x):return x*2
@classmethoddef class_doubler(cls, x): # we need to use 'cls' instead of 'self'; 'cls' reference to the class instead of an instance of the classreturn x*2
@staticmethoddef static_doubler(x): # no need adding 'self' here; static_doubler() could be just a function not inside the classreturn x*2

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

decor = DecoratorTest()
print(decor.doubler(5))# 10
print(decor.class_doubler(5)) # a call with an instance of a class# 10print(DecoratorTest.class_doubler(5)) # a direct call by the class itself# 10
# staticmethod could be called in the same way as classmethod.print(decor.static_doubler(5)) # as an instance of the class# 10print(DecoratorTest.static_doubler(5)) # or as a direct call# 10

这里您可以看到这些方法的一些用例。

奖励:你可以阅读关于@property装饰器这里

tldr;

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

classmethod本质上是可继承的staticmethod

有关详细信息,请参阅其他人的精彩回答。

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

class Employee:
NO_OF_EMPLOYEES = 0  
def __init__(self, first_name, last_name, salary):self.first_name = first_nameself.last_name = last_nameself.salary = salaryself.increment_employees()
def give_raise(self, amount):self.salary += amount
@classmethoddef 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)
@classmethoddef increment_employees(cls):cls.NO_OF_EMPLOYEES += 1
@staticmethoddef get_employee_legal_obligations_txt():legal_obligations = """1. An employee must complete 8 hours per working day2. ..."""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_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: 1Number of employees: 2

静态方法

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

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

print(Employee.get_employee_legal_obligations_txt())

1. An employee must complete 8 hours per working day2. ...

或来自类的实例:

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

1. An employee must complete 8 hours per working day2. ...

参考文献

差异发生在有继承的时候。

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

class Parent:_class_name = "Parent"
@staticmethoddef print_name():print(Parent._class_name)

class Child(Parent):_class_name = "Child"
@staticmethoddef print_name():print(Child._class_name)

Parent.print_name()Child.print_name()

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

class Parent:_class_name = "Parent"
@classmethoddef print_name(cls):print(cls._class_name)

class Child(Parent):_class_name = "Child"

Parent.print_name()Child.print_name()

从留档中定义静态方法和类方法。何时使用静态方法,何时使用类方法。

  • 静态方法就像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:@staticmethoddef 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."""

类方法(函数)->方法

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:@classmethoddef 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 classobject 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.

我想在前面所有答案的基础上补充以下内容,这不是官方的,但符合标准。

首先,您可以考虑始终给予所需的最少权限。因此,如果您不需要特定于实例的东西,请使其成为类方法。如果您不需要特定于类的东西,请使其成为静态方法。

第二件事是考虑你可以通过你创建的方法类型来传达什么。静态方法-帮助函数,旨在在类本身之外使用。类函数-可以在没有实例化的情况下调用,但仅用于该类-否则将是一个静态方法!实例方法-仅用于实例。

这可以帮助您交流模式以及如何使用您的代码。

class Foo:@classmethoddef bar(cls, id: int = None):query = session.query(a.id,a.name,a.address,)
if id is not None:query = query.filter(a.id == id)
return query

例如上面的方法栏没有理由不能是静态的。但是,通过使其成为类方法,您可以传达它应该由类本身使用,而不是作为用于其他地方的辅助函数!

请记住,以上不是官方的,而是我的个人偏好

@classmethod

  • 可以通过cls调用类变量和实例、类和静态方法,也可以直接通过类名调用,但不能通过实例变量调用。

  • 可以通过对象调用,也可以直接通过类名调用。

  • 需要cls作为第一个参数,否则无法调用@classmethod,并且在约定中使用cls的名称,因此其他名称而不是cls仍然有效。

@staticmethod

  • 既可以由对象调用,也可以直接由类名调用。

  • 可以通过类名直接调用类变量和实例、类和静态方法,但不能调用实例变量。

  • 不需要selfcls

*详细地,我还解释了Python中的“实例方法”是什么?<强>我的回答中的实例方法

@classmethod

例如,@classmethod可以通过cls和直接通过类名调用类变量和实例、类和静态方法,@classmethod可以通过对象和直接通过类名调用,如下所示:

class Person:x = "Hello"def __init__(self, name):self.name = name    
@classmethod # Heredef test1(cls):print(cls.x)   # Class variable by `cls`cls.test2(cls) # Instance method by `cls`cls.test3()    # Class method by `cls`cls.test4()    # Static method by `cls`print()print(Person.x)       # Class variable by class namePerson.test2("Test2") # Instance method by class namePerson.test3()        # Class method by class namePerson.test4()        # Static method by class name    
def test2(self):print("Test2")        
@classmethoddef test3(cls):print("Test3")        
@staticmethoddef test4():print("Test4")
obj = Person("John")obj.test1() # By object
# Or
Person.test1() # By class name

输出:

HelloTest2Test3Test4
HelloTest2Test3Test4

而且,@classmethod不能同时通过cls和直接通过类名调用实例变量,所以如果@classmethod尝试通过cls和直接通过类名调用实例变量,如下所示:

# ...    
@classmethoddef test1(cls):print(cls.name) # Instance variable by `cls`        
# Or
print(Person.name) # Instance variable by class name# ...
obj = Person("John")obj.test1()
# Or
Person.test1()

发生以下错误:

属性错误:类型对象'Person'没有属性'name'

如果@classmethod没有cls

# ...    
@classmethoddef test1(): # Without "cls"print("Test1")  
# ...
obj = Person("John")obj.test1()
# Or
Person.test1()

@classmethod无法调用,则发生如下错误:

test1()接受0个位置参数,但给出了1个

而且,在约定中使用cls的名称,因此其他名称而不是cls仍然可以工作,如下所示:

# ...
@classmethoddef test1(orange):print(orange.x)      # Class variableorange.test2(orange) # Instance methodorange.test3()       # Class methodorange.test4()       # Static method
# ...
obj = Person("John")obj.test1()
# Or
Person.test1()

输出:

HelloTest2Test3Test4

@staticmethod

例如,@staticmethod可以通过对象调用,也可以直接通过类名调用,如下所示:

class Person:x = "Hello"def __init__(self, name):self.name = name
@staticmethod # Heredef test1():print("Test1")    
def test2(self):print("Test2")        
@classmethoddef test3(cls):print("Test3")        
@staticmethoddef test4():print("Test4")
obj = Person("John")obj.test1() # By object
# Or
Person.test1() # By class name

输出:

Test1

并且,@staticmethod可以直接通过类名调用类变量和实例、类和静态方法,但不能通过实例变量调用,如下所示:

# ...    
@staticmethoddef test1():print(Person.x)       # Class variablePerson.test2("Test2") # Instance methodPerson.test3()        # Class methodPerson.test4()        # Static method            
# ...
obj = Person("John")obj.test1()
# Or
Person.test1()

输出:

HelloTest2Test3Test4

并且,如果@staticmethod尝试调用实例变量,如下所示:

# ...    
@staticmethoddef test1():print(Person.name) # Instance variable            
# ...
obj = Person("John")obj.test1()
# Or
Person.test1()

发生以下错误:

属性错误:类型对象'Person'没有属性'name'

而且,@staticmethod不需要selfcls,所以如果@staticmethodselfcls,你需要传递一个参数,如下所示:

# ...    
@staticmethoddef test1(self): # With "self"print(self)
# Or
@staticmethoddef test1(cls): # With "cls"print(cls)
# ...
obj = Person("John")obj.test1("Test1") # With an argument
# Or
Person.test1("Test1") # With an argument

输出:

Test1

否则,如果您不传递参数,如下所示:

# ...    
@staticmethoddef test1(self): # With "self"print("Test1")
# Or
@staticmethoddef test1(cls): # With "cls"print("Test1")
# ...
obj = Person("John")obj.test1() # Without an argument
# Or
Person.test1() # Without an argument

会出现以下错误:

test1()缺少1个必需的位置参数:'self'

test1()缺少1个必需的位置参数:'cls'