@classmethod和@staticmethod对初学者的意义?

有人能向我解释@classmethod@staticmethod在python中的含义吗? 我需要知道其中的差异和意义。

据我所知, @classmethod告诉一个类,它是一种应该被继承到子类或者...的方法。 但是,这有什么意义呢? 为什么不定义类方法而不添加@classmethod@staticmethod或任何@定义?

tl; dr:我应该什么时候使用它们,为什么要使用它们,我应该如何使用它们?

我用C ++很先进,所以使用更高级的编程概念应该不成问题。 如果可能,请随意给我一个相应的C ++示例。


尽管classmethodstaticmethod非常相似,但两种实体的用法略有不同: 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 ),该参数保存对新创建实例的引用。

类方法

我们有一些使用classmethod可以很好地完成的任务。

我们假设我们想创建大量的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
  • 静态方法

    staticmethod呢? 它与classmethod非常相似,但不包含任何必需的参数(如类方法或实例方法)。

    我们来看下一个用例。

    我们有一个我们想要以某种方式验证的日期字符串。 这个任务也逻辑上绑定到我们迄今为止使用的Date类,但仍然不需要实例化它。

    这里是staticmethod方法可能有用的地方。 我们来看下一段代码:

        @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呢。


    Rostyslav Dzinko的回答非常合适。 我想我可以突出显示另一个原因,当你创建额外的构造函数时,你应该选择@classmethod over @staticmethod

    在上面的例子中,Rostyslav使用@classmethod from_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_yearmillenium_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
    

    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意思是:当这个方法被调用时,我们传递类作为第一个参数,而不是该类的实例(就像我们通常用方法做的那样)。 这意味着您可以在该方法内使用该类及其属性,而不是特定的实例。

    @staticmethod意思是:当这个方法被调用时,我们不会把类的实例传递给它(就像我们通常用的方法一样)。 这意味着你可以在一个类中放入一个函数,但是你不能访问该类的实例(当你的方法不使用实例时这很有用)。

    链接地址: http://www.djcxy.com/p/1585.html

    上一篇: Meaning of @classmethod and @staticmethod for beginner?

    下一篇: What is the difference between an interface and abstract class?