@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而不是@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

当他/她希望根据调用方法的子类来更改方法的行为时,可以使用@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..."

一个小汇编

@静态方法一种在类中编写方法的方法,而不引用它所调用的对象。因此不需要传递self或cls之类的隐式参数。它的编写方式与在类外部的编写方式完全相同,但在python中没有任何用处,因为如果您需要在类内部封装方法,因为该方法需要是该类的一部分,那么@staticmethod在这种情况下很方便。

@分类法当你想写一个工厂方法并且通过这个自定义属性可以附加在一个类中时,这一点很重要。可以在继承的类中重写此属性。

这两种方法的比较如下

类方法可以修改类状态,它绑定到类并且包含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)

简而言之,@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可以根据需要添加尽可能多的替代构造函数。