用@staticmethod修饰的方法和用@classmethod修饰的方法有什么区别?
当前回答
当存在继承时,就会出现差异。
假设有两个类——父类和子类。如果要使用@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()
其他回答
对iPython中其他相同方法的快速破解表明,@staticmethod产生了边际性能增益(以纳秒为单位),但在其他方面它似乎没有任何作用。此外,在编译过程中通过staticmethod()处理方法的额外工作(这在运行脚本时任何代码执行之前发生)可能会抵消任何性能提升。
为了代码的可读性,我会避免@staticmethod,除非您的方法将用于纳秒计数的工作量。
这是一篇关于这个问题的短文
@staticmethod函数只不过是在类中定义的函数。它可以在不首先实例化类的情况下调用。它的定义通过继承是不可变的。@classmethod函数也可以在不实例化类的情况下调用,但它的定义通过继承遵循子类,而不是父类。这是因为@classmethod函数的第一个参数必须始终是cls(class)。
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官方文档。
@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方法设置为类方法的另一个好处是,子类可以决定更改其实现,这可能是因为它非常通用,可以处理多种类型的集群,因此仅检查类的名称是不够的。
我认为提供一个纯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
推荐文章
- Pandas和NumPy+SciPy在Python中的区别是什么?
- 将列表转换为集合会改变元素的顺序
- 如何在matplotlib更新一个情节
- TypeError: ` NoneType `对象在Python中不可迭代
- 如何在Vim注释掉一个Python代码块
- python标准库中的装饰符(特别是@deprecated)
- 如何从外部访问本地Django web服务器
- 删除字符串的最后3个字符
- 在python中执行no-op的标准方法是什么?
- 如何从生成器构建numpy数组?
- 什么时候我应该(不)想要在我的代码中使用熊猫apply() ?
- 数据类vs类型。NamedTuple主要用例
- 如何从macOS完全卸载蟒蛇
- JavaScript .includes()方法的多个条件
- 是否有可能键入提示一个lambda函数?