用@staticmethod修饰的方法和用@classmethod修饰的方法有什么区别?
当前回答
关于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类方法的事情,但一旦发布,我发现真正需要的是类方法。但要摆脱静态方法为时已晚。
其他回答
@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方法设置为类方法的另一个好处是,子类可以决定更改其实现,这可能是因为它非常通用,可以处理多种类型的集群,因此仅检查类的名称是不够的。
@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'>
我想在前面所有答案的基础上补充以下内容,这不是官方的,但符合标准。
首先,你可以考虑总是给予最少的特权。因此,如果您不需要特定于实例的内容,请将其设置为类方法。如果您不需要特定于类的内容,请将其设置为静态方法。
第二件事是考虑您可以通过创建的方法类型进行通信。静态方法-助手函数意味着在类本身之外使用。类函数-可以在没有实例化的情况下调用,但意味着只能与该类一起使用-否则将是一个静态方法!实例方法-仅用于实例。
这可以帮助您沟通模式以及如何使用代码。
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
例如,上面提到的——没有理由说明方法栏不能是静态的。然而,通过将其设置为类方法,您可以传达它应该由类本身使用,而不是作为其他地方使用的助手函数!
请记住,以上内容不是官方的,而是我个人的偏好
类方法将类作为隐式第一参数接收,就像实例方法接收实例一样。它是一个绑定到类而不是类的对象的方法。它可以访问类的状态,因为它使用指向类而不是对象实例的类参数。它可以修改将应用于类的所有实例的类状态。例如,它可以修改将适用于所有实例的类变量。
另一方面,与类方法或实例方法相比,静态方法不接收隐式第一个参数。并且无法访问或修改类状态。它只属于类,因为从设计的角度来看,这是正确的方法。但就功能而言,在运行时,它并不与类绑定。
作为指导,使用静态方法作为实用程序,使用类方法作为工厂。或者可以定义一个单例。并使用实例方法对实例的状态和行为进行建模。
希望我清楚!
要决定是使用@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
推荐文章
- 输入对象的datetime。Datetime没有Datetime属性
- numpy数组的Python内存使用情况
- NumPy或Pandas:保持数组类型为整数,同时具有NaN值
- 列表理解条件中的' elif '
- Python的“property”和“attribute”有什么区别?
- 在Python中格式化多行字典的正确方法是什么?
- 向异常添加信息?
- Sphinx autodoc不够自动
- 如何限制Django模型中数值字段的最大值?
- 访问对象内存地址
- 为什么在Python方法中需要显式地有“self”参数?
- 如何升级Windows 10下的Python安装?
- tqdm在Jupyter笔记本重复打印新的进度条
- 如何在for循环中注释类型?
- 如何替换一个Pandas数据框架的字符串列中的文本?