Python中@staticmethod和@classmethod的深入解析

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

  • 引入
  • Class Method
  • Static Method
  • 这篇博文主要讲解以下问题:

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

    作者:SoaringPigeon

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python中@staticmethod和@classmethod的深入解析

    发表回复