Python中的静态方法(staticmethod)和类方法(classmethod)都依赖于装饰器 (decorator)来实现。 其中静态方法的用法如下:
class C (object):
&staticmethod
def f (argl, arg2 P ...):
而类方法的用法如下:
class C (object):
@classmethod
def f (clsr argl, arg2, "•):
静态方法和类方法都可以通过 类名.方法名
(如 C.f()
)或者 实例.方法名
( C().f()
)的形式来访问。
其中静态方法没有常规方法的特殊行为,如绑定、非绑定、隐式参数等规则,
而类方法的调用使用类本身作为其隐含参数,但调用本身并不需要显示提供该参数。
class A (object):
def instance_method(self , x):
print ("calling instance method instance_method (%s, %s) "%(self, x))
@classmethod
def class_method (cls,x):
print( "calling class_method(%s,%s)"%(cls,x))
@staticmethod
def static_method(x):
print ("calling static_method(%s)"%x)
a = A()
a.instance_method("test")
calling instance method instance_method (<__main__.A object at 0x7f5e18655070>, test)
a.class_method("test")
calling class_method(<class '__main__.A'>,test)
a.static_method("test")
calling static_method(test)
上面的例子是类方法和静态方法的简单应用,
从程序的输出可以看出虽然类方法在调用的时候没有显式声明cls
,
但实际上类本身是作为隐含参数传入的。
classmethod的适用场景
在了解完静态方法和类方法的基本知识之后再来研究这样一个问题:
为什么需要静态方法和类方法,它们和普通的实例方法之间存在什么区别?
通过对具体问题的研究来回答这些问题,假设有水果类Fruit,
它用属性 total
表示总量,Fruit中已经有方法 set()
来设置总量,
print_total()
方法来打印水果数 Se
类 Apple
和类 Orange
继承自Fruit。
需要分別跟踪不同类型的水果的总量。有好几种方法可以实现这个功能。
方法一: 利用普通的实例方法来实现。
在Apple和Orange类中分別定义类变量 total
,然后再植盖基类的 set()
和 print_total()
方法,
但这会导致代码冗余,因为本质上这些方法所实现的功能相同(读者可以自行完成)。
方法二: 使用类方法实现,具体实现代码清单如下。
class Fruit(object):
def __init__ (self, area="", category="",batch=""):
self.area = area
self.category = category
self . batch = batch
@staticmethod
def Init_Product(product_info):
area, category, batch = map(int, product_info.split('-'))
fruit = Fruit (area, category, batch)
return fruit
total = 0
@classmethod
def print_total(cls):
print (cls.total) #
print (id(Fruit.total))
print (id(cls.total))
@classmethod
def set (cls, value):
#print *'calling class^method (%s^ %s) w% (clsr value)
cls.total = value
class Apple(Fruit):
pass
class Orange(Fruit):
pass
appl = Apple ()
appl.set (200)
app2 = Apple()
org1= Orange()
org1.set(300)
org2 = Orange()
appl.print_total ()
200 94033145588264 94033145594664
org1.print_total()
300 94033145588264 140042107812240
简单分析可知,针对不同种类的水果对象调用 set()
方法的时候隐形传入的参数为该对象所对应的类,
在调用 set()
的过程中动态生成了对应的类的类变量。这就是 classmethod
的妙处。
再来看一个必须使用类方法而不是静态方法的例子:假设对于每一个Fruit类提供3个实例属性;
area
表示区域代码,category
表示种类代码,batch
表示批次号。
现需要一个方法能够将以area-category-batch
形式表示的字符串形式的输入转化为对应的属性并以对象返回。
假设Fruit中有如下初始化方法,并且有静态方法 Init_Product()
能够满足上面所提的要求。
def __init__ (self, area="", category="",batch=""):
self.area = area
self.category = category
self . batch = batch
@staticmethod
def Init_Product(product_info):
area, category, batch = map(int, product_info.split('-'))
fruit = Fruit (area, category, batch)
return fruit
首先来看看使用静态方法所带来的问题。
app1=Apple(2,5,10)
org1=Orange.Init_Product("3-3-9")
print appl is instance of Apple: w+str (i$instance appl, Apple))
print orgl is instance of Orange; w+str {isixistance (orgl , Orange))
运行程序会发现 isinstance(org1,Orange)
的值为 False
。这不奇怪,
因为静态方法实际相当于一个定义在类里面的函数,Init_Product
返冋的实际是Fruit的对象,
所以它不会是 Orange的实例。Init_Product()
的功能类似于工厂方法,
能够根据不同的类型返回对应的类的实例,因此使用静态方法并不能获得期望的结果,
类方法才是正确的解决方案。可以针对代码做出如下修改:
@classmethod
def Init_Product(els,rproductinfo):
area, category, batch = map(int, product_info.split("-"))
fruit =els(arear ,category, batch)
return fruit
def is_input_valid(product_info):
area, category, batch = map(inproduct_info.split('-'))
try:
assert 0 <= area <=10
assert 0 <=category <= 15
assert 0 <= batch <= 99
except AssertionError:
return False
return True
那么应该将其声明为静态方法还是类方法呢?答案是两者都可, 甚至将其作为一个定义在类的外部的函数都是可以的。 但仔细分析该方法会发现它既不跟特定的实例相关也不跟特 定的类相关, 因此将其定义为静态方法是个不错的选择,这样代码能够一目了然。 也许会提问:为什么不将该方法定义成外部函数呢?这是因为静态方法定义在类中, 较之外部函数,能够使其有效地将代码组织起来,从而使相关代码的垂直距离更近, 提高代码的可维护性。 当然,如果有一组独立的方法,将其定义在一个模块中, 通过模块来访问这些方法也是一个不错的选择。