@classmethod和@staticmethod在Python中是什么意思,它们有什么不同?我应该何时使用它们,为什么要使用它们,以及如何使用它们?
据我所知,@classmethod告诉一个类,它是一个应该继承到子类中的方法,或者。。。某物然而,这有什么意义?为什么不直接定义class方法而不添加@classmethod或@staticmethod或任何@定义?
@classmethod和@staticmethod在Python中是什么意思,它们有什么不同?我应该何时使用它们,为什么要使用它们,以及如何使用它们?
据我所知,@classmethod告诉一个类,它是一个应该继承到子类中的方法,或者。。。某物然而,这有什么意义?为什么不直接定义class方法而不添加@classmethod或@staticmethod或任何@定义?
当前回答
@分类法
@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)
其他回答
@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和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类实例的典型初始化器,它作为一个典型的实例方法接收参数,具有第一个非可选参数(self),该参数保存对新创建实例的引用。
Class方法
我们有一些任务可以使用类方法很好地完成。
假设我们要创建许多Date类实例,这些实例的日期信息来自外部源,编码为“dd-mm-yyyy”格式的字符串。假设我们必须在项目源代码的不同位置执行此操作。
因此,我们在这里必须做的是:
分析一个字符串,以接收日、月和年作为三个整数变量或由该变量组成的三项元组。通过将这些值传递给初始化调用来实例化Date。
这将看起来像:
day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)
为此,C++可以通过重载实现这样的特性,但Python缺少这种重载。相反,我们可以使用classmethod。让我们创建另一个构造函数。
@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')
让我们更仔细地看看上面的实现,并回顾一下我们在这里的优势:
我们在一个地方实现了日期字符串解析,现在可以重用了。封装在这里工作得很好(如果您认为可以在其他地方将字符串解析作为单个函数来实现,则此解决方案更适合OOP范式)。cls是类本身,而不是类的实例。这很酷,因为如果我们继承了Date类,所有的孩子都将定义from_string。
静态方法
静态方法呢?它与classmethod非常相似,但不接受任何强制参数(就像类方法或实例方法那样)。
让我们看看下一个用例。
我们有一个日期字符串,我们想以某种方式验证它。这个任务也在逻辑上绑定到我们目前使用的Date类,但不需要实例化它。
这里是静态方法可能有用的地方。让我们看下一段代码:
@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
# usage:
is_date = Date.is_date_valid('11-09-2012')
因此,正如我们从staticmethod的用法中看到的那样,我们无法访问类是什么——它基本上只是一个函数,在语法上像方法一样被调用,但无法访问对象及其内部(字段和其他方法),而类方法确实具有这样的功能。
当他/她希望根据调用方法的子类来更改方法的行为时,可以使用@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..."
罗斯季斯拉夫·德津科的回答非常恰当。我想我可以强调另一个原因,当您创建一个额外的构造函数时,您应该选择@classmethod而不是@staticmethod。
在本例中,Rostyslav使用@classmethodfrom_string作为Factory,从其他不可接受的参数创建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和million_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的实例?世界跆拳道联盟?这是因为使用了@staticmethoddecorator。
在大多数情况下,这是不希望的。如果你想要的是一个知道调用它的类的Factory方法,那么@classmethod就是你需要的。
将Date.millenium重写为(这是上述代码中唯一更改的部分):
@classmethod
def millenium(cls, month, day):
return cls(month, day, 2000)
确保课程不是硬编码的,而是学习的。cls可以是任何子类。结果对象将是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