静态方法、类方法和属性方法
在 Python 中有三种常用的方法装饰器(参考装饰器部分内容),可以使普通的类实例方法变成带有特殊功能的方法,分别是静态方法、类方法和属性方法。
静态方法 / Static Method
在 def 定义之前加上 @staticmethod 进行装饰,便可以使该方法成为静态方法,静态方法无法调用实例属性。静态方法可以通过实例或类进行调用。通常用于无需生成实例或无需共享实例属性的方法中,比如一些纯粹的数学公式计算等场合。
1 class Foo(): 2 @staticmethod 3 def foo(a, b): 4 print(‘This is a staticmethod, cal result: %d‘ % (a+b)) 5 6 Foo.foo(1, 2) 7 Foo().foo(3, 4)
代码中定义了一个简单的 Foo 类,并通过添加静态方法装饰器定义了一个 foo 静态方法,该方法会显示出传入的两个参数的计算结果,由于整个方法中不需要用到实例或实例的属性,因此可以定义为一个静态方法。
This is a staticmethod, cal result: 3 This is a staticmethod, cal result: 7
从输出的结果看到,无论是使用类还是类实例,都可以对静态方法进行调用。
类方法 / Class Method
在 def 定义之前加上 @classmethod 进行装饰,使得该方法成为类方法,类方法同样无法调用实例属性, 但是可以调用类属性,类方法可以通过实例或类进行调用。同时,类方法还可以用于在类实例化之前对类进行一些处理。
1 class Foo: 2 num = 1 3 4 @classmethod 5 def instance(cls): 6 cls.num = 7 7 cls = cls() 8 return cls 9 10 def __init__(self): 11 self.foo = Foo.num 12 13 def test(self): 14 print(‘This is a test‘) 15 16 cls_1 = Foo() 17 print("foo is: %d, num is: %d" % (cls_1.foo, cls_1.num)) 18 19 # Change num from 1 to 7 before __init__ 20 cls_2 = Foo.instance() 21 print("foo is: %d, num is: %d" % (cls_2.foo, cls_2.num)) 22 cls_2.test()
上面的代码中,定义了一个类方法,这个类方法会在调用的时候修改 Foo 类的 num 属性,随后在生成一个类实例(第 7 行,这步实际上完成了一个类的初始化)并返回。首先不调用类方法,直接生成一个类实例 cls_1,随后查看 cls_1 中的属性值,然后在利用类方法,通过 Foo 直接调用 instance 方法生成一个类实例 cls_2,并同样的查看类实例中的属性,最后调用 test 方法来测试这个类实例是否可以和正常生成的类实例一样调用实例方法。
foo is: 1, num is: 1 foo is: 7, num is: 7 This is a test
最后的输出结果可以看出,直接生成的类实例 cls_1 中的属性并没有被改变,而通过类实例方法生成的类实例 cls_2 中的属性则变化了,同时这个 cls_2 也同样能够调用普通的实例方法。也就是说类方法可以通过类进行直接调用而不需要先经过实例化,同时类方法传入的第一个参数 cls 即为当前的类。
Note: 这种类方法可以用在单例模式(参考单例模式)中,确保一个类无论实例化多少次都只有一个实例对象存在。
属性方法 / Property Method
在 def 定义之前加上 @property 进行装饰,使得该方法成为属性方法,属性方法可以在外部像调用属性一样直接进行调用,若需要实现对属性方法值的修改,则还需要定义由 @methodname.setter 装饰的同名方法,在这个方法内实现对属性方法返回值的修改。
1 class Foo(object): 2 3 def __init__(self): 4 self._foo = None 5 6 @property 7 def foo(self): 8 return self._foo 9 10 @foo.setter 11 def foo(self, value): 12 if isinstance(value, int): 13 self._foo = (value + 0.5) 14 15 if __name__ == "__main__": 16 f = Foo() 17 print("foo is %s" % f.foo) 18 f.foo = 1 19 print("foo is %s" % f.foo)
上面的代码中定义了一个 foo 的属性方法和属性方法的 setter,这样就是的 foo 这个方法使用起来就如同是一个属性一般,可以直接通过实例进行调用(第17 / 19 行),而 @foo.setter 装饰器也赋予了 foo 属性方法赋值的能力,可以通过类似属性赋值的方式直接给 foo 传递一个参数(第 18 行),并且此时还可以对这个传入的参数进行一定的操作(如第 12 / 13 行)。
foo is None foo is 1.5
最终输出的结果中可以看出,foo 虽然是一个方法,但是其使用的方式却完全类似于一个属性,这中属性方法所提供的功能在面向对象编程的时候十分有效,常常可以通过定义好属性方法来实现一系列对象的操作,使得一个实例更加接近真实对象的行为方式(上面的例子可能不算十分恰当,不过可以理解为一个识别器,只要传入的是数字,便修改自身的 _foo 属性为该值+0.5,而在外部则无需关心这一实现,只把 foo 当做是一个属性赋值即可)。