@classmethod和@staticmethod在Python中是什么意思,它们有什么不同?我应该何时使用它们,为什么要使用它们,以及如何使用它们?
据我所知,@classmethod告诉一个类,它是一个应该继承到子类中的方法,或者。。。某物然而,这有什么意义?为什么不直接定义class方法而不添加@classmethod或@staticmethod或任何@定义?
@classmethod和@staticmethod在Python中是什么意思,它们有什么不同?我应该何时使用它们,为什么要使用它们,以及如何使用它们?
据我所知,@classmethod告诉一个类,它是一个应该继承到子类中的方法,或者。。。某物然而,这有什么意义?为什么不直接定义class方法而不添加@classmethod或@staticmethod或任何@定义?
当前回答
简而言之,@classmethod将普通方法转换为工厂方法。
让我们用一个例子来探讨一下:
class PythonBook:
def __init__(self, name, author):
self.name = name
self.author = author
def __repr__(self):
return f'Book: {self.name}, Author: {self.author}'
如果没有@classmethod,您应该一个接一个地创建实例,并且它们是分散的。
book1 = PythonBook('Learning Python', 'Mark Lutz')
In [20]: book1
Out[20]: Book: Learning Python, Author: Mark Lutz
book2 = PythonBook('Python Think', 'Allen B Dowey')
In [22]: book2
Out[22]: Book: Python Think, Author: Allen B Dowey
例如@classmethod
class PythonBook:
def __init__(self, name, author):
self.name = name
self.author = author
def __repr__(self):
return f'Book: {self.name}, Author: {self.author}'
@classmethod
def book1(cls):
return cls('Learning Python', 'Mark Lutz')
@classmethod
def book2(cls):
return cls('Python Think', 'Allen B Dowey')
测试它:
In [31]: PythonBook.book1()
Out[31]: Book: Learning Python, Author: Mark Lutz
In [32]: PythonBook.book2()
Out[32]: Book: Python Think, Author: Allen B Dowey
看见在类定义中成功创建实例,并将它们收集在一起。
总之,@classmethoddecorator将传统方法转换为工厂方法,使用classmethods可以根据需要添加尽可能多的替代构造函数。
其他回答
@分类法
@classmethod可以与__init__进行比较。你可以认为这是另一个__init__()。这是python在c++中实现类构造函数重载的方式。
class C:
def __init__(self, parameters):
....
@classmethod
def construct_from_func(cls, parameters):
....
obj1 = C(parameters)
obj2 = C.construct_from_func(parameters)
注意,它们都有一个类的引用作为definitioin中的第一个参数,而init_使用self,但constructfrom_func使用cls。
@静态方法
@静态方法可以与对象方法进行比较
class C:
def __init__(self):
....
@staticmethod
def static_method(args):
....
def normal_method(parameters):
....
result = C.static_method(parameters)
result = obj.normal_method(parameters)
何时使用每个
@staticmethod函数只不过是在类中定义的函数。它可以在不首先实例化类的情况下调用。它的定义通过继承是不可变的。
Python不必为对象实例化绑定方法。它简化了代码的可读性:看到@staticmethod,我们知道该方法不依赖于对象本身的状态;
@classmethod函数也可以在不实例化类的情况下调用,但它的定义遵循子类,而不是父类,通过继承,可以被子类重写。这是因为@classmethod函数的第一个参数必须始终是cls(class)。
工厂方法,用于使用例如某种预处理为类创建实例。静态方法调用静态方法:如果将静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法
这里有一个很好的链接。
简而言之,@classmethod将普通方法转换为工厂方法。
让我们用一个例子来探讨一下:
class PythonBook:
def __init__(self, name, author):
self.name = name
self.author = author
def __repr__(self):
return f'Book: {self.name}, Author: {self.author}'
如果没有@classmethod,您应该一个接一个地创建实例,并且它们是分散的。
book1 = PythonBook('Learning Python', 'Mark Lutz')
In [20]: book1
Out[20]: Book: Learning Python, Author: Mark Lutz
book2 = PythonBook('Python Think', 'Allen B Dowey')
In [22]: book2
Out[22]: Book: Python Think, Author: Allen B Dowey
例如@classmethod
class PythonBook:
def __init__(self, name, author):
self.name = name
self.author = author
def __repr__(self):
return f'Book: {self.name}, Author: {self.author}'
@classmethod
def book1(cls):
return cls('Learning Python', 'Mark Lutz')
@classmethod
def book2(cls):
return cls('Python Think', 'Allen B Dowey')
测试它:
In [31]: PythonBook.book1()
Out[31]: Book: Learning Python, Author: Mark Lutz
In [32]: PythonBook.book2()
Out[32]: Book: Python Think, Author: Allen B Dowey
看见在类定义中成功创建实例,并将它们收集在一起。
总之,@classmethoddecorator将传统方法转换为工厂方法,使用classmethods可以根据需要添加尽可能多的替代构造函数。
@classmethod的意思是:当调用此方法时,我们将类作为第一个参数传递,而不是该类的实例(我们通常使用方法)。这意味着您可以在该方法中使用类及其财产,而不是特定的实例。
@staticmethod意味着:当调用此方法时,我们不会将类的实例传递给它(就像我们通常使用方法一样)。这意味着您可以将函数放在类中,但不能访问该类的实例(当您的方法不使用实例时,这很有用)。
@classmethod和@staticmethod的含义?
方法是对象名称空间中的函数,可作为属性访问。常规(即实例)方法获取实例(我们通常称其为self)作为隐式第一个参数。类方法获取类(我们通常称之为cls)作为隐式第一个参数。静态方法没有得到隐式的第一个参数(像正则函数)。
我应该何时使用它们,为什么要使用它们,以及如何使用它们?
你不需要任何一个装饰器。但是,基于应该最小化函数的参数数量的原则(请参见Clean Coder),它们对于实现这一点非常有用。
class Example(object):
def regular_instance_method(self):
"""A function of an instance has access to every attribute of that
instance, including its class (and its attributes.)
Not accepting at least one argument is a TypeError.
Not understanding the semantics of that argument is a user error.
"""
return some_function_f(self)
@classmethod
def a_class_method(cls):
"""A function of a class has access to every attribute of the class.
Not accepting at least one argument is a TypeError.
Not understanding the semantics of that argument is a user error.
"""
return some_function_g(cls)
@staticmethod
def a_static_method():
"""A static method has no information about instances or classes
unless explicitly given. It just lives in the class (and thus its
instances') namespace.
"""
return some_function_h()
对于实例方法和类方法,不接受至少一个参数是TypeError,但不理解该参数的语义是用户错误。
(定义某些函数,例如:
some_function_h = some_function_g = some_function_f = lambda x=None: x
这将起作用。)
实例和类上的虚线查找:
实例上的虚线查找按以下顺序执行:
类名称空间中的数据描述符(如属性)实例__dict中的数据__类名称空间(方法)中的非数据描述符。
注意,实例上的虚线查找是这样调用的:
instance = Example()
instance.regular_instance_method
方法是可调用的属性:
instance.regular_instance_method()
实例方法
参数self是通过虚线查找隐式给出的。
必须从类的实例访问实例方法。
>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>
类方法
参数cls是通过虚线查找隐式给出的。
您可以通过实例或类(或子类)访问此方法。
>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>
静态方法
未隐式给出任何参数。此方法的工作方式与(例如)在模块名称空间上定义的任何函数类似,但它可以被查找
>>> print(instance.a_static_method())
None
同样,我什么时候应该使用它们,为什么要使用它们?
与实例方法相比,这些方法中的每一个在传递方法的信息方面都越来越严格。
当你不需要这些信息时使用它们。
这使您的函数和方法更易于推理和单元测试。
哪个更容易推理?
def function(x, y, z): ...
or
def function(y, z): ...
or
def function(z): ...
参数较少的函数更容易推理。它们也更容易进行单元测试。
这些类似于实例、类和静态方法。记住,当我们有一个实例时,我们也有它的类,再次问问自己,哪个更容易推理?:
def an_instance_method(self, arg, kwarg=None):
cls = type(self) # Also has the class of instance!
...
@classmethod
def a_class_method(cls, arg, kwarg=None):
...
@staticmethod
def a_static_method(arg, kwarg=None):
...
内置示例
下面是几个我最喜欢的内置示例:
str.maketrans静态方法是字符串模块中的一个函数,但从str命名空间访问它要方便得多。
>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'
dict.fromkeys类方法返回一个从可迭代键实例化的新字典:
>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}
当进行子类化时,我们看到它以类方法的形式获取类信息,这非常有用:
>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'>
我的建议-结论
当您不需要类或实例参数,但函数与对象的使用相关,并且函数位于对象的命名空间中时,可以使用静态方法。
当您不需要实例信息,但需要类信息(可能是其他类或静态方法的类信息,也可能是构造函数本身的类信息)时,请使用类方法。(您不会对类进行硬编码,以便在此处使用子类。)