@classmethod与@staticmethod区别
在Python中有3种方式定义类方法分别是常规方式、@classmethod修饰方式、@staticmethod修饰方式。
接下来分别对3种不同方式的定义举例说明。
普通方法: 其实就是需要操作一些实例独有的属性,是实例而不是类。第一个参数一般是隐式地将实例传递给self参数。
class People():  
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def greeting(self):
        return f'Hello, {self.name}'
p = People('Anders', 'Male')  
p.greeting()
# 输出内容:
# 'Hello, Anders'
@staticmethod: 静态方法其实就是一个普通的函数(静态方法无法调用任何非静态成员,所以也不需要self参数),可以使用类名直接调用,很多人不太明白的是为什么不直接把静态方法放在类外调用呢,毕竟效果一样,但是从代码逻辑从属来说,静态方法是一种组织或风格特征。如果这个方法实现的功能与类比较相关的话,放到了类里面更合适,这代表是某个类专用的工具函数。
class People():  
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    @staticmethod
    def greeting(name, gender):
        return f'Hello, {name}!'
print(People.greeting('Anders', 'Male'))
# 输出内容:
# Hello, Anders!
静态方法调用另一个静态方法直接用「类名.静态方法()」调用即可。如果想在静态方法中调用类中的其它非静态方法,或者非静态属性必须要通过生成实例对象的方式去访问。
class Web(object):  
    def __init__(self):
        self.desc = "实例属性,不共享"
    def norm_method(self):
        """普通方法"""
        print('普通方法被调用!')
    # 另外一个静态方法
    @staticmethod
    def foo_staticmethod_other():
        print('另外一个静态方法被调用!')
    @staticmethod
    def foo_staticmethod():
        # 调用非静态方法和属性
        instance = Web()
        instance.norm_method()
        print(instance.desc)
        # 调用另一个静态方法
        print(Web.foo_staticmethod_other())
@classmethod: 类方法可以通过类或者实例对象调用。一般情况下是作用于类相关的操作。第一个参数不是类的实例对象,而必须是类对象,在Python中这个参数常被写作cls,因为全称class是保留字。而被@classmethod修饰的函数内可调用类属性,但不能调用实例属性。
注意:对类作出的任何改变会对它的所有实例对象产生影响。
我们看到下面创建了3次实例对象,当最后去访问类方法person时,count值累积为数值3,并没有还原。
class People():  
    count = 0
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        People.count += 1
    @classmethod
    def person(cls):
        return cls.count
p1 = People('Anders', 'Male')  
p2 = People('Mary', 'Female')  
p3 = People('James', 'Male')
print(f'People has {People.person()} little person objects.')
# 输出内容:
# People has 3 little person objects.
那么类方法一般用在什么场景呢,举个如下例子,如果用户输入的时间日期格式和我们预期的纯数字不一致,比如'2019-03-03',那可以先使用get_date类方法对日期字符串进行处理,然后在使用时date = cls(year, month, day)做构造函数初始化并创建了一个date实例,最后我们把它return返回出来,然后赋值给result变量后就可以直接调用类实例方法了。
class Time_Data:  
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def get_date(cls, data_as_string):
        year, month, day = map(int, data_as_string.split('-'))
        date = cls(year, month, day)
        return date
    def out_date(self):
        print(f'The date is: {self.year}/{self.month}/{self.day}')
result = Time_Data.get_date('2019-03-03')  
result.out_date()
# 输出内容:
# The date is: 2019/3/3
总结起来就是,class method可以用来为一个类创建一些预处理的实例。
