三分钟透彻理解Python中的@staticmethod和@classmethod

三分钟透彻理解Python中的@staticmethod和@classmethod

这篇博文主要讲解以下问题:

@classmethod和@staticmethod在Python中是什么意思,它们有何区别?我应该在何时使用它们,为什么要使用它们,以及如何使用它们?

@classmethod告诉一个类,这是一个应该被子类继承的方法,或者某种程度上是这样。然而,这样做的目的是什么呢?为什么不直接定义类方法,而不添加@classmethod、@staticmethod或任何@定义呢?

引入

虽然@classmethod和@staticmethod非常相似,但它们在用法上有轻微的区别:classmethod必须将一个类对象的引用作为第一个参数,而staticmethod可以不带任何参数

Example:

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')

Explanation:
让我们假设一个处理日期信息的类的示例(这将是我们的模板):

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 Method

有些任务可以很好地使用classmethod完成。

假设我们想要创建大量的Date类实例,这些实例包含来自外部源的日期信息,以字符串格式 ‘dd-mm-yyyy’ 编码。假设我们在项目源代码的不同位置都需要执行这个操作。

因此,在这里我们需要做的是:

1、解析字符串以获取日期、月份和年份,将它们作为三个整数变量或一个包含这些变量的3项元组。
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. 封装在这里运作良好(如果您认为您可以在其他地方将字符串解析实现为一个单独的函数,那么这个解决方案更符合面向对象编程范式)。
  3. cls 代表的是类本身,而不是类的实例。这相当酷,因为如果我们继承了我们的 Date 类,所有的子类也都会有 from_string 方法。

Static Method

那么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却有这种权限。

如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。也就是说在classmethod中可以调用类中定义的其他方法、类的属性,但staticmethod只能通过A.a调用类的属性,但无法通过在该函数内部调用A.foo2()。用代码加以说明:

class A(object):
    a = 'a'
    @staticmethod
    def foo1(name):
        print 'hello', name
        print A.a # 正常
        print A.foo2('mamq') # 报错: unbound method foo2() must be called with A instance as first argument (got str instance instead)
    def foo2(self, name):
        print 'hello', name
    @classmethod
    def foo3(cls, name):
        print 'hello', name
        print A.a
        print cls().foo2(name)
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SoaringPigeon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值