用@staticmethod修饰的方法和用@classmethod修饰的方法有什么区别?
静态方法是一种对所调用的类或实例一无所知的方法。它只获取传递的参数,而不是隐式的第一个参数。它在Python中基本上是无用的——您可以只使用模块函数而不是静态方法。
另一方面,类方法是一种方法,它将被调用的类或被调用的实例的类作为第一个参数传递。当您希望该方法成为类的工厂时,这很有用:因为它获得了作为第一个参数调用的实际类,所以即使涉及子类,您也可以始终实例化正确的类。例如,观察类方法dict.fromkeys()在子类上调用时如何返回子类的实例:
>>> class DictSubclass(dict):
... def __repr__(self):
... return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>
@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'>
这是一篇关于这个问题的短文
@staticmethod函数只不过是在类中定义的函数。它可以在不首先实例化类的情况下调用。它的定义通过继承是不可变的。@classmethod函数也可以在不实例化类的情况下调用,但它的定义通过继承遵循子类,而不是父类。这是因为@classmethod函数的第一个参数必须始终是cls(class)。
也许一些示例代码会有所帮助:注意foo、class_foo和static_foo的调用签名的不同:
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。事实上,如果你定义了类方法,这可能是因为您打算从类而不是从类实例调用它。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)
静态方法用于将与类有某种逻辑联系的函数分组到该类。
foo只是一个函数,但当你调用.foo时,你不只是得到函数,您将得到函数的“部分应用”版本,其中对象实例a作为函数的第一个参数。foo需要2个参数,而a.foo只需要1个参数。
a绑定到foo。这就是以下术语“约束”的含义:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
对于.class_foo,a不绑定到class_foo,而类a绑定到class-foo。
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
这里,对于staticmethod,即使它是一个方法,a.static_foo也只返回一个没有参数约束的好的ole函数。static_foo需要1个参数,并且.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官方文档:
@分类法
类方法将类作为隐式第一个参数,就像instance方法接收该实例。要声明类方法,请使用成语:C类:@分类法定义f(cls,arg1,arg2,…):。。。@classmethod表单是一个函数decorator–请参见函数中的函数定义详细定义。它可以在类上调用(例如C.f())或实例(例如C().f())。实例是除了它的类之外,都被忽略。如果类方法用于派生类,派生类对象是作为隐含的第一个参数传递。类方法不同于C++或Java静态方法。如果你愿意这些,请参见本文中的staticmethod()部分
@静态方法
静态方法不接收隐式第一个参数。声明静态方法,使用这个成语:C类:@静态方法定义f(arg1,arg2,…):。。。@staticmethod表单是一个函数decorator–请参见函数中的函数定义详细定义。它可以在类上调用(例如C.f())或实例(例如C().f())。实例是除了它的类之外,都被忽略。Python中的静态方法类似类似于Java或C++中的那些。对于更高级的概念,请参见classmethod()。
@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)
还要注意,这是一个使用类方法和静态方法的好例子,静态方法显然属于类,因为它在内部使用了类Cluster。类方法只需要关于类的信息,而不需要对象的实例。
将_is_cluster_for方法设置为类方法的另一个好处是,子类可以决定更改其实现,这可能是因为它非常通用,可以处理多种类型的集群,因此仅检查类的名称是不够的。
对iPython中其他相同方法的快速破解表明,@staticmethod产生了边际性能增益(以纳秒为单位),但在其他方面它似乎没有任何作用。此外,在编译过程中通过staticmethod()处理方法的额外工作(这在运行脚本时任何代码执行之前发生)可能会抵消任何性能提升。
为了代码的可读性,我会避免@staticmethod,除非您的方法将用于纳秒计数的工作量。
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_nonormal_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,它是从Python 3中的字符串模块移动来的。它使转换表适合str.translate使用。从字符串的实例中使用它似乎很愚蠢,如下所示,但从字符串模块导入函数相当笨拙,能够从类中调用它很好,如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'
Class方法
类方法与实例方法相似,因为它接受隐式的第一个参数,但不是接受实例,而是接受类。为了更好地使用语义,它们通常被用作替代构造函数,它将支持继承。
内置类方法最典型的例子是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'>
请参阅panda源代码以了解其他类似的替代构造函数示例,也可以参阅关于classmethod和staticmethod的Python官方文档。
我认为一个更好的问题是“你什么时候会使用@classmethod vs@staticmethod?”
@classmethod允许您轻松访问与类定义关联的私有成员。这是一种很好的方法来实现单实例,或者控制所创建对象实例数量的工厂类。
@staticmethod提供了边际性能增益,但我还没有看到静态方法在类内的有效使用,而静态方法不能作为类外的独立函数实现。
关于如何在Python中使用静态、类或抽象方法的权威指南是本主题的一个很好的链接,总结如下。
@staticmethod函数只不过是在类中定义的函数。它可以在不首先实例化类的情况下调用。它的定义通过继承是不可变的。
Python不必为对象实例化绑定方法。它简化了代码的可读性,并且不依赖于对象本身的状态;
@classmethod函数也可以在不实例化类的情况下调用,但它的定义遵循子类,而不是父类,通过继承,可以被子类重写。这是因为@classmethod函数的第一个参数必须始终是cls(class)。
工厂方法,用于使用例如某种预处理为类创建实例。静态方法调用静态方法:如果将静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法
要决定是使用@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
我将用一个例子来解释基本的区别。
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-我们可以直接调用静态和类方法,而无需初始化
# A.run_self() # wrong
A.run_static()
A.run_class()
2-静态方法不能调用self方法,但可以调用其他静态和类方法
3-静态方法属于类,根本不会使用对象。
4-类方法不绑定到对象,而是绑定到类。
关于staticmethod vs classmethod的另一个考虑是继承。假设你有以下课程:
class Foo(object):
@staticmethod
def bar():
return "In Foo"
然后您需要在子类中重写bar():
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
这是可行的,但请注意,现在子类(Foo2)中的bar()实现不能再利用该类特有的任何特性。例如,假设Foo2有一个名为magic()的方法,您希望在Foo2实现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()中调用Foo2.magic(),但随后您重复自己的操作(如果Foo2的名称发生更改,您必须记住更新bar()方法)。
对我来说,这稍微违反了开放/封闭原则,因为在Foo中做出的决定会影响您重构派生类中的公共代码的能力(即扩展的开放性较低)。如果bar()是一个类方法,我们就可以了:
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
此外:历史笔记:Guido Van Rossum(Python的创建者)曾将静态方法称为“意外”: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
老实说,staticmethod是一个错误——我试图做一些类似Java类方法的事情,但一旦发布,我发现真正需要的是类方法。但要摆脱静态方法为时已晚。
静态方法:
没有自参数的简单函数。处理类属性;而不是实例属性。可以通过类和实例调用。内置函数staticmethod()用于创建它们。
静态方法的优点:
它在类作用域中本地化函数名它将功能代码移动到使用位置附近与模块级函数相比,导入更方便,因为不必专门导入每个方法@静态方法定义some_static_method(*args,**kwds):通过
类方法:
将第一个参数作为类名的函数。可以通过类和实例调用。这些是用内置函数中的classmethod创建的。@分类法定义some_class_method(cls,*args,**kwds):通过
@classmethod:可用于创建对该类创建的所有实例的共享全局访问。。。。。比如由多个用户更新记录。。。。我特别发现它在创建单件时也很有用。:)
@静态方法:与关联的类或实例无关。。。但为了可读性,可以使用静态方法
我开始用C++学习编程语言,然后是Java,然后是Python,所以这个问题也困扰了我,直到我理解了每种语言的简单用法。
类方法:Python不像Java和C++那样没有构造函数重载。为了实现这一点,可以使用classmethod。以下示例将对此进行解释
让我们考虑一个Person类,它接受两个参数first_name和last_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
您可以使用类名调用
Person.validate_name("Gaurang Shah")
顾名思义,类方法用于更改类而不是对象。要对类进行更改,它们将修改类属性(而不是对象属性),因为这就是更新类的方式。这就是类方法将类(通常用“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."
分析@staticmethod,提供不同的见解。
类的普通方法是一种隐式动态方法,它将实例作为第一个参数。相反,静态方法不将实例作为第一个参数,因此称为“static”。
静态方法确实是一个与类定义之外的函数相同的正常函数。幸运的是,它被分组到类中,只是为了更接近应用它的位置,或者您可以滚动查找它。
让我先告诉一下用@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',)
我的贡献展示了@classmethod、@staticmethod和实例方法之间的区别,包括实例如何间接调用@staticmmethod。但是,与其从实例间接调用@staticmethod,不如将其私有化可能更“Python化”。这里没有演示从私有方法获取内容,但基本上是相同的概念。
#!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)
'''
"""
您可能需要考虑以下两者之间的区别:
class A:
def foo(): # no self parameter, no decorator
pass
and
class B:
@staticmethod
def foo(): # no self parameter
pass
这在python2和python3之间发生了变化:
蟒蛇2:
>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
蟒蛇3:
>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
因此,在python3中,对仅直接从类调用的方法使用@staticmethod已成为可选的。如果要从类和实例调用它们,仍然需要使用@staticmethoddecorator。
其他案例都被未用的答案很好地涵盖了。
只有第一个参数不同:
normal方法:当前对象作为(附加)第一个参数自动传递classmethod:当前对象的类自动作为(附加的)第一个参数传递staticmethod:不会自动传递额外的参数。传递给函数的就是得到的。
更详细地说。。。
正常方法
“标准”方法,如在所有面向对象的语言中。当调用对象的方法时,会自动为其提供一个额外的参数self作为其第一个参数。即,方法
def f(self, x, y)
必须使用2个参数调用。self是自动传递的,它是对象本身。类似于这个神奇地出现在例如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)
我认为提供一个纯Python版本的staticmethod和classmethod将有助于在语言级别上理解它们之间的区别(请参阅Descriptor Howto Guide)。
这两个都是非数据描述符(如果您首先熟悉描述符,那么更容易理解它们)。
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
类方法将类作为隐式第一参数接收,就像实例方法接收实例一样。它是一个绑定到类而不是类的对象的方法。它可以访问类的状态,因为它使用指向类而不是对象实例的类参数。它可以修改将应用于类的所有实例的类状态。例如,它可以修改将适用于所有实例的类变量。
另一方面,与类方法或实例方法相比,静态方法不接收隐式第一个参数。并且无法访问或修改类状态。它只属于类,因为从设计的角度来看,这是正确的方法。但就功能而言,在运行时,它并不与类绑定。
作为指导,使用静态方法作为实用程序,使用类方法作为工厂。或者可以定义一个单例。并使用实例方法对实例的状态和行为进行建模。
希望我清楚!
staticmethod无法访问继承层次结构中对象、类或父类的属性。它可以直接在类中调用(无需创建对象)。
classmethod无法访问对象的属性。但是,它可以访问继承层次结构中的类和父类的属性。它可以直接在类中调用(无需创建对象)。如果在对象处调用,则它与不访问self的普通方法相同<属性>并访问self__第__类<属性>。
假设我们有一个b=2的类,我们将创建一个对象,并将其重新设置为b=4。Staticmethod无法访问以前的任何内容。Classmethod只能通过cls.b访问.b==2。正常方法可以通过self访问:.b==4和.b==2__第__.b类。
我们可以遵循KISS风格(保持简单,愚蠢):不要使用静态方法和类方法,不要在没有实例化它们的情况下使用类,只访问对象的属性self.attribute。有些语言是这样实现OOP的,我认为这不是坏主意
一个非常重要的实际差异发生在子类化时。如果你不介意的话,我会劫持@unsubu的例子:
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)
注意,这并不意味着您不能在静态方法中使用其他方法,您只需直接引用类,这意味着子类的静态方法仍将引用父类:
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)
实例方法:
+可以修改对象实例状态
+可以修改类状态
类方法:
-无法修改对象实例状态
+可以修改类状态
静态方法:
-无法修改对象实例状态
-无法修改类状态
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
Python带有几个内置的装饰器。三大类是:
@classmethod
@staticmethod
@property
首先,让我们注意,类的任何函数都可以用这个类的实例调用(在初始化这个类之后)。
@classmethod是一种方法,它不仅可以作为类的实例调用函数,还可以直接由类本身作为其第一个参数调用函数。
@staticmethod是一种将函数放入类的方法(因为它在逻辑上属于类),同时表示它不需要访问类(因此我们不需要在函数定义中使用self)。
让我们考虑一下以下课程:
class DecoratorTest(object):
def __init__(self):
pass
def doubler(self, x):
return x*2
@classmethod
def class_doubler(cls, x): # we need to use 'cls' instead of 'self'; 'cls' reference to the class instead of an instance of the class
return x*2
@staticmethod
def static_doubler(x): # no need adding 'self' here; static_doubler() could be just a function not inside the class
return x*2
让我们看看它是如何工作的:
decor = DecoratorTest()
print(decor.doubler(5))
# 10
print(decor.class_doubler(5)) # a call with an instance of a class
# 10
print(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
# 10
print(DecoratorTest.static_doubler(5)) # or as a direct call
# 10
这里您可以看到这些方法的一些用例。
奖金:您可以在这里阅读@property decorator
首先,让我们从一个示例代码开始,我们将使用它来理解这两个概念:
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
Class方法
类方法接受类本身作为隐式参数,以及(可选地)定义中指定的任何其他参数。重要的是要理解类方法不能访问对象实例(就像实例方法一样)。因此,类方法不能用于更改实例化对象的状态,而是能够更改该类的所有实例之间共享的类状态。当我们需要访问类本身时,类方法通常很有用——例如,当我们想要创建工厂方法时,即创建类实例的方法。换句话说,类方法可以作为替代构造函数。
在我们的示例代码中,可以通过提供三个参数来构造Employee的实例;first_name、last_name和薪水。
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_eemployee_legal_entributions_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. ...
工具书类
Python中静态方法和类方法的区别是什么?
当存在继承时,就会出现差异。
假设有两个类——父类和子类。如果要使用@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()
从其文档中定义静态方法和类方法。以及何时使用静态方法和何时使用类方法。
静态方法类似于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(函数)->方法
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.
我想在前面所有答案的基础上补充以下内容,这不是官方的,但符合标准。
首先,你可以考虑总是给予最少的特权。因此,如果您不需要特定于实例的内容,请将其设置为类方法。如果您不需要特定于类的内容,请将其设置为静态方法。
第二件事是考虑您可以通过创建的方法类型进行通信。静态方法-助手函数意味着在类本身之外使用。类函数-可以在没有实例化的情况下调用,但意味着只能与该类一起使用-否则将是一个静态方法!实例方法-仅用于实例。
这可以帮助您沟通模式以及如何使用代码。
class Foo:
@classmethod
def 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
例如,上面提到的——没有理由说明方法栏不能是静态的。然而,通过将其设置为类方法,您可以传达它应该由类本身使用,而不是作为其他地方使用的助手函数!
请记住,以上内容不是官方的,而是我个人的偏好
@类方法:
可以通过cls和直接通过类名调用类变量和实例、类和静态方法,但不能通过实例变量。可以通过对象和类名直接调用。第一个参数需要cls,否则无法调用@classmethod,并且按照惯例使用cls的名称,因此其他名称而不是cls仍然有效。
@静态方法:
既可以由对象调用,也可以直接由类名调用。可以通过类名而不是实例变量直接调用类变量和实例、类和静态方法。不需要self或cls。
*在回答Python中什么是“实例方法”时,我还详细解释了实例方法?
@类方法:
例如,@classmethod可以通过cls和直接通过类名调用类变量和实例、类和静态方法,@classmethod可以通过对象和直接通过类名称调用,如下所示:
class Person:
x = "Hello"
def __init__(self, name):
self.name = name
@classmethod # Here
def 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 name
Person.test2("Test2") # Instance method by class name
Person.test3() # Class method by class name
Person.test4() # Static method by class name
def test2(self):
print("Test2")
@classmethod
def test3(cls):
print("Test3")
@staticmethod
def test4():
print("Test4")
obj = Person("John")
obj.test1() # By object
# Or
Person.test1() # By class name
输出:
Hello
Test2
Test3
Test4
Hello
Test2
Test3
Test4
而且,@classmethod不能同时通过cls和直接通过类名调用实例变量,因此如果@classmethod试图同时通过cls和直接通过类名称调用实例变量(如下所示):
# ...
@classmethod
def 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()
出现以下错误:
AttributeError:类型对象“Person”没有属性“name”
如果@classmethod没有cls:
# ...
@classmethod
def test1(): # Without "cls"
print("Test1")
# ...
obj = Person("John")
obj.test1()
# Or
Person.test1()
@无法调用classmethod,则出现如下错误:
TypeError:test1()采用0个位置参数,但给出了1个
而且,cls的名称在约定中使用,因此其他名称而不是cls仍然有效,如下所示:
# ...
@classmethod
def test1(orange):
print(orange.x) # Class variable
orange.test2(orange) # Instance method
orange.test3() # Class method
orange.test4() # Static method
# ...
obj = Person("John")
obj.test1()
# Or
Person.test1()
输出:
Hello
Test2
Test3
Test4
@静态方法:
例如,@staticmethod既可以按对象调用,也可以按类名直接调用,如下所示:
class Person:
x = "Hello"
def __init__(self, name):
self.name = name
@staticmethod # Here
def test1():
print("Test1")
def test2(self):
print("Test2")
@classmethod
def test3(cls):
print("Test3")
@staticmethod
def test4():
print("Test4")
obj = Person("John")
obj.test1() # By object
# Or
Person.test1() # By class name
输出:
Test1
而且,@staticmethod可以通过类名直接调用类变量和实例、类和静态方法,而不是实例变量,如下所示:
# ...
@staticmethod
def test1():
print(Person.x) # Class variable
Person.test2("Test2") # Instance method
Person.test3() # Class method
Person.test4() # Static method
# ...
obj = Person("John")
obj.test1()
# Or
Person.test1()
输出:
Hello
Test2
Test3
Test4
并且,如果@staticmethod尝试调用实例变量,如下所示:
# ...
@staticmethod
def test1():
print(Person.name) # Instance variable
# ...
obj = Person("John")
obj.test1()
# Or
Person.test1()
出现以下错误:
AttributeError:类型对象“Person”没有属性“name”
而且,@staticmethod不需要self或cls,因此如果@staticmmethod具有self或cl,则需要传递如下所示的参数:
# ...
@staticmethod
def test1(self): # With "self"
print(self)
# Or
@staticmethod
def test1(cls): # With "cls"
print(cls)
# ...
obj = Person("John")
obj.test1("Test1") # With an argument
# Or
Person.test1("Test1") # With an argument
输出:
Test1
否则,如果不传递如下所示的参数:
# ...
@staticmethod
def test1(self): # With "self"
print("Test1")
# Or
@staticmethod
def test1(cls): # With "cls"
print("Test1")
# ...
obj = Person("John")
obj.test1() # Without an argument
# Or
Person.test1() # Without an argument
出现以下错误:
TypeError:test1()缺少1个必需的位置参数:“self”
TypeError:test1()缺少1个必需的位置参数:“cls”
推荐文章
- 如何删除Python中的前导空白?
- python中的assertEquals和assertEqual
- 如何保持Python打印不添加换行符或空格?
- PHP中接口的意义是什么?
- 为什么Python的无穷散列中有π的数字?
- Python 3.7数据类中的类继承
- 如何在PyTorch中初始化权重?
- 计数唯一的值在一列熊猫数据框架像在Qlik?
- 使用Pandas将列转换为行
- 从matplotlib中的颜色映射中获取单个颜色
- 虚拟方法和抽象方法的区别
- 将Pandas或Numpy Nan替换为None以用于MysqlDB
- 使用pandas对同一列进行多个聚合
- 使用Python解析HTML
- django MultiValueDictKeyError错误,我如何处理它