• python的@classmethod和@staticmethod


    本文是对StackOverflow上的一篇高赞回答的不完全翻译,原文链接:meaning-of-classmethod-and-staticmethod-for-beginner

    Python面向对象编程中,类中定义的方法可以是@classmethod 装饰的类方法,也可以是@staticmethod 装饰的静态方法,用的最多的还是不带装饰器的实例方法。为方便,在下文中用@classmethod装饰的类方法将直接用@classmethod来表述,@staticmethod同理,望读者在阅读时自行加以区分。

    @classmethod和@staticmethod很相似,它们装饰的方法在使用上只有一点区别:@classmethod装饰的方法第一个参数必须是一个类(通常为cls),而@staticmethod装饰的方法则按业务需求设置参数,也可以根本没有参数。

    样例

    样例是一个处理日期信息的类,如下:

    1. class Date(object):
    2.  
    3. def __init__(self, day=0, month=0, year=0):
    4. self.day = day
    5. self.month = month
    6. self.year = year

    这个类可以用来存储指定日期(不包括时区信息,假设所有日期都是UTC时间)。

            这个类有一个__init__函数用来初始化实例对象,它的第一个必须的参数self指向一个已创建的Date类的实例对象,这个方法是一个典型的实例方法。

    Class Method

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

    假设我们要从一堆有着特定日期格式的字符串(如'dd-mm-yyyy')创建很多对应的Date类的实例,而且在项目的各个地方都要进行这样的转换。那么我们要做的是:

    1. 解析一个字符串来得到day,month,year这三个整数变量或者组装出一个tuple

    2. 把这些值传递给初始化函数来实例化Date实例对象

    比如:

    1. day, month, year = map(int, string_date.split('-'))
    2. date1 = Date(day, month, year)

    要实现这个目的,C++可以使用重载,但是Python没有这样的语法,但是可以使用@classmethod来实现,如下:

    1. @classmethod
    2. def from_string(cls, date_as_string):
    3. day, month, year = map(int, date_as_string.split('-'))
    4. date1 = cls(day, month, year)
    5. return date1
    6.  
    7. date2 = Date.from_string('11-09-2012')

    仔细比较这两种方法,使用@classmethod有以下优点:

    1. 我们只写了一个转换字符串的方法,而且这个方法是可重用的。

    2. 把这个方法封装在类中,更紧密(也许你会认为可以写一个单独的函数去转换字符串,但是使用@classmethod更符合面向对象的思维)。

    3. cls 是类本身的对象,而不是类的实例对象,这样的话继承自Date的对象都会有from_string这个方法。

    Static Method

    那么@staticmethod呢?其实它跟@classmethod非常相似,只是它没有任何必需的参数。

    假设我们要去检验一个日期的字符串是否有效。这个任务与Date类相关,但是又不需要Date实例对象,在这样的情况下@staticmethod就可以派上用场了。如下:

    1. @staticmethod
    2. def is_date_valid(date_as_string):
    3. day, month, year = map(int, date_as_string.split('-'))
    4. return day <= 31 and month <= 12 and year <= 3999
    5.  
    6. # usage:
    7. is_date = Date.is_date_valid('11-09-2012')

    从上面的用法可以看出,它只是一个功能,调用的语法和一般的方法调用一样,也不访问实例对象那和它的内部字段和方法。

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    以下有错误的地方

    类的普通方法

    class Animal(object):
        def __init__(self,name):
            self.name = name
        def intro(self):
            print('there is a %s'%(self.name))
    cat = Animal('cat')
    cat.intro()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    静态类方法

    class Animal(object):
        def __init__(self,name):
            self.name = name
        @staticmethod
        def intro(self):
            print('there is a %s'%(self.name))
    cat = Animal('cat')
    cat.intro()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    加上装饰器后运行会报错,原因是方法变为一个普通函数,脱离的与类的关系,不能引用构造函数中的变量了。
    加上装饰器后运行会报错
    使用场景举例:python内置方法os中的方法,可以直接使用的工具包,跟类没关系。


    class Animal(object):
        def __init__(self,name):
            self.name = name
        @classmethod
        def intro(self):
            print('there is a %s'%(self.name))
    cat = Animal('cat')
    cat.intro()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    报错信息
    这里写图片描述
    如果换成

    class Animal(object):
        name = 'cat'
        def __init__(self,name):
            self.name = name
        @classmethod
        def intro(self):
            print('there is a %s'%(self.name))
    cat = Animal('cat')
    cat.intro()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以正常运行。
    结论:类方法只能调用类变量,不能调用实例变量


    属性方法@property 把一个方法变为(伪装成)类属性。因为类属性的实质是一个类变量,用户可以调用变量就可以修改变量。某些特定场景要限制用户行为,就用到静态方法。
    @property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。(摘自廖雪峰的博客)

    class Animal(object):
        def __init__(self,name):
            self.name = name
        @property
        def intro(self,food):
            print('there is a %s eating %s'%(self.name,food))
    cat = Animal('cat')
    cat.intro()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    报错:这里写图片描述
    方法不能正常调用。如果要调用,如下:

    cat.intro
    • 1

    但是这样的话,方法就没办法单独传入参数。如果要传入参数,如下:

    class Animal(object):
        def __init__(self,name):
            self.name = name
        @property
        def intro(self):
            print('there is a %s eating %s'%(self.name,food))
        @intro.setter
        def intro(self,food):
            pass
    cat = Animal('cat')
    cat.intro
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    cat.intro还有其他操作getter deleter等等。

  • 相关阅读:
    pip install 下载慢的问题
    virtualenv 和 virtualenvwrapper 实践
    一个采用python获取股票数据的开源库,相当全,及一些量化投资策略库
    qos
    Rxjava, RxAndroid, Retrofit 等库的使用
    一个程序员直播的地方
    Android Studio 中SDK Manager的设置
    ipython, 一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数
    Nagle算法,tcp小包组合(延迟)发送的算法
    RESTful到底是什么玩意??
  • 原文地址:https://www.cnblogs.com/fengff/p/9587952.html
Copyright © 2020-2023  润新知