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

2024-04-25 04:46:09 发布

您现在位置:Python中文网/ 问答频道 /正文

有人能给我解释一下python中@classmethod@staticmethod的含义吗?我需要知道区别和意义。

据我所知,@classmethod告诉类它是一个应该继承到子类中的方法,或者。。。一些东西。然而,这又有什么意义呢?为什么不直接定义类方法而不添加@classmethod@staticmethod或任何@定义呢?

tl;dr:何时应该使用它们,为什么应该使用它们,以及如何使用它们?

我对C++很在行,所以使用更高级的编程概念不应该是个问题。如果可能的话,请给我一个相应的C++示例。


Tags: 方法概念示例定义编程子类意义classmethod
3条回答

@classmethod表示:当调用此方法时,我们将类作为第一个参数传递,而不是作为该类的实例传递(就像我们通常对方法所做的那样)。这意味着您可以在该方法中使用类及其属性,而不是特定的实例。

@staticmethod意味着:当调用这个方法时,我们不会向它传递类的实例(就像我们通常对方法所做的那样)。这意味着您可以将函数放在类中,但不能访问该类的实例(当您的方法不使用该实例时,这非常有用)。

尽管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表示)。

这里有一个Python类实例的典型初始值设定项__init__,它以典型的instancemethod形式接收参数,第一个非可选参数(self)保存对新创建实例的引用。

类方法

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

假设我们要创建许多类实例,其中的日期信息来自外部源,编码为格式为“dd-mm-yyy”的字符串。假设我们必须在项目的源代码中的不同位置执行此操作。

所以我们必须做的是:

  1. 解析一个字符串,将日、月和年作为三个整数变量或由该变量组成的三项元组接收。
  2. 通过将这些值传递给初始化调用来实例化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')

让我们更仔细地看一下上面的实现,并回顾一下我们在这里有哪些优势:

  1. 我们在一个地方实现了日期字符串解析,现在可以重用了。
  2. 封装在这里工作得很好(如果您认为可以在其他地方将字符串解析作为单个函数实现,那么这个解决方案更适合OOP范式)。
  3. 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有。

罗斯特拉夫·钦科的回答非常恰当。我想我可以强调另一个原因,当您创建一个额外的构造函数时,应该选择@classmethod而不是@staticmethod

在上面的例子中,Rostyslav使用@classmethodfrom_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_yearmillenium_new_year都是Date类的实例。

但是,如果您仔细观察,不管怎样,工厂流程都是硬编码来创建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的实例吗?世界跆拳道联盟?嗯,那是因为使用了@staticmethod装饰器。

在大多数情况下,这是不希望的。如果您想要的是一个工厂方法,它知道调用它的类,那么@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

相关问题 更多 >