@classmethod和@staticmethod在Python中是什么意思,它们有什么不同?我应该何时使用它们,为什么要使用它们,以及如何使用它们?
据我所知,@classmethod告诉一个类,它是一个应该继承到子类中的方法,或者。。。某物然而,这有什么意义?为什么不直接定义class方法而不添加@classmethod或@staticmethod或任何@定义?
@classmethod和@staticmethod在Python中是什么意思,它们有什么不同?我应该何时使用它们,为什么要使用它们,以及如何使用它们?
据我所知,@classmethod告诉一个类,它是一个应该继承到子类中的方法,或者。。。某物然而,这有什么意义?为什么不直接定义class方法而不添加@classmethod或@staticmethod或任何@定义?
当前回答
我是这个网站的初学者,我已经阅读了以上所有答案,并得到了我想要的信息。然而,我没有投票权。所以我想从StackOverflow开始,得到我所理解的答案。
@staticmethod不需要self或cls作为方法的第一个参数@staticmethod和@classmethod包装函数可以由实例或类变量调用@staticmethod修饰函数会影响某种“不可变属性”,子类继承无法覆盖其基类函数,该基类函数由@staticmethoddecorator封装。@classmethod需要cls(类名,如果需要,可以更改变量名,但不建议)作为函数的第一个参数@classmethod总是以子类的方式使用,子类继承可能会改变基类函数的效果,即@classmethod包装的基类函数可能会被不同的子类覆盖。
其他回答
简而言之,@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的用法中看到的那样,我们无法访问类是什么——它基本上只是一个函数,在语法上像方法一样被调用,但无法访问对象及其内部(字段和其他方法),而类方法确实具有这样的功能。
何时使用每个
@staticmethod函数只不过是在类中定义的函数。它可以在不首先实例化类的情况下调用。它的定义通过继承是不可变的。
Python不必为对象实例化绑定方法。它简化了代码的可读性:看到@staticmethod,我们知道该方法不依赖于对象本身的状态;
@classmethod函数也可以在不实例化类的情况下调用,但它的定义遵循子类,而不是父类,通过继承,可以被子类重写。这是因为@classmethod函数的第一个参数必须始终是cls(class)。
工厂方法,用于使用例如某种预处理为类创建实例。静态方法调用静态方法:如果将静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法
这里有一个很好的链接。
一种稍微不同的思考方式可能对某人有用。。。在超类中使用类方法来定义该方法在被不同的子类调用时的行为。当我们想要返回相同的东西而不管我们调用的子类是什么时,就使用静态方法。
类方法可以修改类状态,它绑定到类并且包含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)