rank vote view answer url
66 1321 508348 11 url

对于初学者如何理解 @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:

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

这个 class 很显然是用来存储特定日期的信息.

这里用 Python 的 __init__ 来初始化类实例, 它可以接收参数作为一个 instancemethod, 它的第一个参数 self 是新建实例的引用.

类方法

我们可以用 classmethod 来很好的完成一些功能.

假设我们想通过一些形如 dd-mm-yyyy 的 string 来创建 Date 实例.而且我们需要在我们工程里许多不同的地方实现.

所以我们必须:

  1. 解析 string 为天,月和年这3个整数或者3元元祖.
  2. 通过传递这些值来完成 Date 的初始化调用
day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

在 C++ 里可以用 overloading 的方法来实现, 但是 Python 里没有 overloading.取而代之的我们可以使用 classmethod.让我们来创建另一种 constructor

    @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. 我们在一个地方实现了 date string 的转换并且可以复用.
  2. 很好的封装(如果你认为或许可以实现一个函数来解析, 但是这么做更符合 OOP 设计)
  3. cls 是一个承载 class 自己的对象, 并不是 class 的实例.这个非常好用,因为只要我们继承了 Data class, 所有的子类都拥有 from_string 这个方法.

静态方法

staticmethods 呢?它非常像 classmethod 但是它不包含任何必须的参数(比如说类方法或者实例方法).

让我们看看下一个用法.

我们想验证一个 date string 的有效性,这个任务不需要绑定到实例上只需要绑定到类上面.

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 就想一个函数一样不需要指定 class,语法上的调用也非常像函数,没办法访问对象及其内部,而 classmethod 就可以.