有人可以给我解释python中
@classmethod
和@staticmethod
的含义吗?我需要知道区别和含义。 据我了解,
@classmethod
告诉一个类,它是一个应该继承到子类中的方法,或...。但是,这有什么意义呢?为什么不定义类方法而不添加@classmethod
或@staticmethod
或任何@
定义?tl; dr:dr:我什么时候应该使用它们,为什么要使用它们,以及应该如何使用它们? />
#1 楼
尽管classmethod
和staticmethod
非常相似,但是两个实体的用法略有不同:classmethod
必须将对类对象的引用作为第一个参数,而staticmethod
根本不能具有任何参数。示例
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
@staticmethod
def is_date_valid(date_as_string):
day, month, year = map(int, date_as_string.split('-'))
return day <= 31 and month <= 12 and year <= 3999
date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')
解释
让我们假设一个类的例子,处理日期信息(这将是我们的样板):
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
该类显然可以用于存储有关某些日期的信息(没有时区信息;假定所有日期均以UTC表示)。
这里具有
__init__
(一个典型的Python类实例的初始化程序),它以典型的instancemethod
的形式接收参数,并具有第一个非可选参数(self
),该参数持有对新创建的实例的引用。Class Method
我们有一些可以使用
classmethod
s很好地完成的任务。假设我们要创建很多q4312 079q类实例,其日期信息来自外部源,其日期信息编码为字符串,格式为“ dd-mm-yyyy”。假设我们必须在项目的源代码中的不同位置执行此操作。
所以我们在这里必须做的是:
解析字符串以接收日,月和年为三个整数变量或由该变量组成的三项元组。
通过将这些值传递给初始化调用来实例化
Date
。看起来像:为此,C ++可以通过重载实现这种功能,但是Python缺少这种重载。相反,我们可以使用
Date
。让我们创建另一个“构造函数”。day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)
让我们更仔细地看一下上述实现,并在这里回顾一下我们具有的优势:
我们已经在一个地方实现了日期字符串解析,现在可以重用了。
封装在这里可以很好地工作(如果您认为可以将字符串解析作为其他函数实现为其他功能,则此解决方案将更好地适合OOP范例)。
classmethod
是保存类本身的对象,而不是保存类的对象类的实例。这很酷,因为如果我们继承cls
类,则所有子代也会定义Date
。静态方法
from_string
怎么样?它与staticmethod
非常相似,但不带任何强制性参数(像类方法或实例方法一样)。让我们看下一个用例。
我们有我们想要以某种方式验证的日期字符串。从逻辑上讲,此任务也已绑定到我们到目前为止使用的
classmethod
类,但不需要实例化。这里是
Date
有用的地方。让我们看下一段代码: @classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
date2 = Date.from_string('11-09-2012')
因此,正如我们从
staticmethod
的用法可以看到的那样,我们无权访问该类是什么- -它基本上只是一个函数,在语法上像方法一样被调用,但是不能访问对象及其内部(字段和其他方法),而classmethod可以。#2 楼
Rostyslav Dzinko的答案非常恰当。我以为我可以强调另一个原因,即在创建其他构造函数时应选择@classmethod
而不是@staticmethod
。在上面的示例中,Rostyslav使用
@classmethod
from_string
作为工厂从其他方式创建Date
对象,否则该对象是无法接受的参数。使用@staticmethod
可以完成相同的操作,如下面的代码所示: class Date:
def __init__(self, month, day, year):
self.month = month
self.day = day
self.year = year
def display(self):
return "{0}-{1}-{2}".format(self.month, self.day, self.year)
@staticmethod
def millenium(month, day):
return Date(month, day, 2000)
new_year = Date(1, 1, 2013) # Creates a new Date object
millenium_new_year = Date.millenium(1, 1) # also creates a Date object.
# Proof:
new_year.display() # "1-1-2013"
millenium_new_year.display() # "1-1-2000"
isinstance(new_year, Date) # True
isinstance(millenium_new_year, Date) # True
因此
new_year
和millenium_new_year
都是Date
类的实例。但是,如果仔细观察,无论如何,Factory进程都被硬编码来创建
Date
对象。这意味着即使Date
类是子类,这些子类仍将创建普通的Date
对象(没有子类的任何属性)。请参见以下示例: class DateTime(Date):
def display(self):
return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)
datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)
isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # False
datetime1.display() # returns "10-10-1990 - 00:00:00PM"
datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class for more details.
datetime2
不是DateTime
的实例吗? WTF?好吧,这是因为使用了@staticmethod
装饰器。在大多数情况下,这是不希望的。如果您想要的是知道调用它的类的Factory方法,则需要
@classmethod
。将
Date.millenium
重写为(这是上面代码中唯一更改的部分): @classmethod
def millenium(cls, month, day):
return cls(month, day, 2000)
确保
class
并非硬编码而是学习的。 cls
可以是任何子类。生成的object
恰好是cls
的一个实例。让我们测试一下:
datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)
isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # True
datetime1.display() # "10-10-1990 - 00:00:00PM"
datetime2.display() # "10-10-2000 - 00:00:00PM"
原因是,正如您现在所知道的,使用的是
@classmethod
而不是@staticmethod
评论
在解释@classmethod的作用时,说它实际上是工厂方法最有用。
–t3chb0t
18-11-3在17:29
什么是cls?按照上面的答案-cl可以是任何子类。结果对象将正确地是cls的实例。 cls是调用Date或Datetime的对象或方法吗?请解释。
– Rishi jain
19-10-15在11:14
@rishijain就像self如何引用实例一样,cls引用类-例如cls(month,day,2000)== DateTime(month,day,2000)
–同胞
4月20日11:48
这个答案确实阐明了@classmethod对于支持继承的工厂方法的实用性:创建带有一些预处理等的调用类的实例。
–Christabella Irwanto
7月2日8:46
#3 楼
@classmethod
的意思是:调用此方法时,我们将类作为第一个参数传递,而不是该类的实例(通常使用方法)。这意味着您可以在该方法而不是特定实例中使用该类及其属性。@staticmethod
表示:调用此方法时,我们不会将类的实例传递给它(如我们通常使用方法)。这意味着您可以在一个类中放置一个函数,但不能访问该类的实例(这在您的方法不使用该实例时非常有用)。#4 楼
何时使用每个@staticmethod
函数不过是在类内部定义的函数。可调用而无需先实例化该类。它的定义通过继承是不可变的。Python不必实例化对象的绑定方法。
它简化了代码的可读性:看到@staticmethod,我们知道该方法不依赖于对象本身的状态; <br />
@classmethod
函数也可以在不实例化该类的情况下调用,但是其定义遵循子类,而不是父类(通过继承),可以被子类覆盖。这是因为@classmethod
函数的第一个参数必须始终是cls (class)
。工厂方法,这些方法用于使用例如某种预处理。
静态方法调用静态方法:如果将静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法
这里很好链接到该主题。
评论
感谢您比公认的答案更快地达到目标。
– John R Perry
19年8月22日在1:54
#5 楼
@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
的示例,例如: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): ...
def function(y, z): ...
或
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
静态方法是string
模块中的一个函数,但是从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'>
我的建议-结论
当不需要类或实例参数,但该函数与对象的使用有关时,请使用静态方法,并且该函数位于对象的名称空间中很方便。
当您不需要实例信息但可能需要其类或其他统计信息的类信息时,请使用类方法ic方法,或者本身就是构造函数。 (您不会对类进行硬编码,因此可以在此处使用子类。)
评论
通过简洁的解释和微观示例之间的恰当平衡,这是非常清楚的。
– abalter
18 Mar 21 '18 at 10:43
当您不需要实例信息,但可能需要其类的其他类或静态方法,或者本身需要作为构造函数时,请使用类方法。 (您不会对类进行硬编码,以便可以在此处使用子类。)正是我想要的。我将使用实际答案的原因。
–TheRealFakeNews
18-10-11在23:53
这是最好的解释。谢谢!
–piby180
19年8月20日在8:27
#6 楼
当他/她想根据哪个子类正在调用方法来更改方法的行为时,可以使用@classmethod
。请记住,我们在类方法中有对调用类的引用。使用静态方法时,您希望行为在子类之间保持不变
示例:
class Hero:
@staticmethod
def say_hello():
print("Helllo...")
@classmethod
def say_class_hello(cls):
if(cls.__name__=="HeroSon"):
print("Hi Kido")
elif(cls.__name__=="HeroDaughter"):
print("Hi Princess")
class HeroSon(Hero):
def say_son_hello(self):
print("test hello")
class HeroDaughter(Hero):
def say_daughter_hello(self):
print("test hello daughter")
testson = HeroSon()
testson.say_class_hello() #Output: "Hi Kido"
testson.say_hello() #Outputs: "Helllo..."
testdaughter = HeroDaughter()
testdaughter.say_class_hello() #Outputs: "Hi Princess"
testdaughter.say_hello() #Outputs: "Helllo..."
#7 楼
少量编译@staticmethod
一种在类内编写方法而无需引用被调用对象的方法。因此,无需传递诸如self或cls之类的隐式参数。
它的写法与在类外部的写法完全相同,但是在python中并没有用,因为自此以来,如果您需要将方法封装在类中方法必须是该类的一部分,在这种情况下@staticmethod会派上用场。
@classmethod
当您要编写工厂方法并通过此自定义属性来使用时,这一点很重要)可以附加在一个类中。可以在继承的类中重写此属性。
这两种方法之间的比较如下所示
#8 楼
@classmethod@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
,而construct_from_func
通常使用cls
。@staticmethod
@staticmethod
可与object method
比较class C:
def __init__(self):
....
@staticmethod
def static_method(args):
....
def normal_method(parameters):
....
result = C.static_method(parameters)
result = obj.normal_method(parameters)
#9 楼
我是该网站的初学者,已经阅读了以上所有答案,并获得了所需的信息。但是,我无权投票。因此,我想从我了解的答案开始使用StackOverflow。@staticmethod
不需要self或cls作为方法的第一个参数@staticmethod
和@classmethod
包装的函数可以由实例或类变量调用@staticmethod
装饰函数会影响子类继承不能覆盖其基类函数的某种“不可变属性”由@staticmethod
装饰器包裹。 @classmethod
需要cls(类名,如果需要,可以更改变量名,但不建议使用)作为函数的第一个参数@classmethod
始终以子类方式使用,子类继承可能会改变基类函数的效果,即@classmethod
包装的基类函数可能会被不同的子类覆盖。评论
我不确定在这种情况下不可变是什么意思?
–菲利普·阿德勒(Philip Adler)
17年7月23日在11:59
#10 楼
思考它的方式略有不同,这可能对某人有用...类方法用于超类中,以定义当不同的子类调用该方法时应如何表现。无论我们要调用的子类如何,当我们要返回相同的内容时,将使用静态方法。#11 楼
简而言之,@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
看到了吗?在类定义内成功创建实例并将它们收集在一起。
总而言之,@classmethod装饰器将常规方法转换为工厂方法,使用类方法可根据需要添加尽可能多的替代构造器。 。
评论
如果您创建没有类方法的方法,仍然可以达到相同的结果,这不是真正的区别
– Shailyn Ortiz
18年1月1日在17:10
#12 楼
类方法可以修改类状态,它绑定到类并且包含cls作为参数。静态方法不能修改类状态,绑定到类并且它不知道类或实例
class empDetails:
def __init__(self,name,sal):
self.name=name
self.sal=sal
@classmethod
def increment(cls,name,none):
return cls('yarramsetti',6000 + 500)
@staticmethod
def salChecking(sal):
return sal > 6000
emp1=empDetails('durga prasad',6000)
emp2=empDetails.increment('yarramsetti',100)
# output is 'durga prasad'
print emp1.name
# output put is 6000
print emp1.sal
# output is 6500,because it change the sal variable
print emp2.sal
# output is 'yarramsetti' it change the state of name variable
print emp2.name
# output is True, because ,it change the state of sal variable
print empDetails.salChecking(6500)
评论
“ from_string”方法将“ Date”类(不是Date对象)作为第一个参数“ cls”,并通过调用cls(day,month,year)返回构造函数,该方法等效于Date(day,month,year)和返回一个Date对象。
–巴胡巴里语Patil
19年6月2日在10:30
那么可以将类方法视为“替代构造函数”吗?我认为这是迄今为止最好的解释!
– Cryptoharf84
19年8月28日在17:45
对于您的类方法示例,您是否无法通过将from_string定义为字符串方法而不是类方法来完全完成上述操作,然后不调用cls(day,month,year),而是调用Date(day,月,年)?我想这里使用类方法的唯一好处是,如果您希望该方法可用于可能继承并期望from_string对继承的类起作用的子类,对吗?还是我错过了您的要点?
–乔什
4月26日在3:55
@ Josh--stringmethod需要一个现有的对象来工作,所以您不能做同样的事情。正如Crypoharf84所提到的,这允许构造对象的另一种机制。这似乎类似于Dart语言中的“命名构造函数”,后者提供了用于为同一类创建多个构造函数的机制。不同的构造函数允许轻松创建API,例如Date.from_json,Date.from_sql,Date.from_file等。
– DarrylG
5月28日19:53