首先,你要明白什么是装饰器。
之前写过一篇,水平有限,不明不白,如果不懂,自行百度。
静态方法和类方法都是装饰器。
先贴段官方文档:https://docs.python.org/3/library/functions.html
本文参考原文以及图片来自于:https://www.pythoncentral.io/difference-between-staticmethod-and-classmethod-in-python/
@staticmethod
改变方法为静态方法
一个静态方法不需要收到一个明确的第一参数(类中的self)。用下面的语法来声明静态方法:
class C:
@staticmethod
def f(arg1, arg2, ...): ...
再次说明,可以不写参数,不必要传self(一般类中的函数都是要传self的)
@staticmethod本质是个个装饰器——在函数定义中查看函数的描述的详细说明。
他可以在类上(C.F())或者一个实例(C().F())上被调用,类以外的实例被忽略。
静态方法在Python中与他在Java或者C++中很相似。同样的看classmethod用来创造类的构造器。
就像装饰器一样,他同样可以调用静态方法像常规的函数一样,并用他的结果做点什么。在一些情况下需要从类本体中引用函数,并且希望避免自动转换为实例方法。对于这样的情况,可以这么写:
class C:
builtin_open = staticmethod(open)
@classmethod
转换一个方法为类方法
一个类方法接收class作为一个明确的第一参数(cls),就像一个实例方法收到实例一样。声明一个类方法如下:
class C:
@classmethod
def f(cls, arg1, arg2, ...): ...
@classmethod本质是个个装饰器——在函数定义中查看函数的描述的详细说明。
他可以在类上(C.F())或者一个实例(C().F())上被调用,类以外的实例被忽略。如果一个类方法被派生类方法调用,派生类对象忽略第一个参数。
上面两段介绍来自文档,基本介绍了静态方法和类方法是什么,如果不懂继续往下看。
类的运行流程
第4步,我们不需要为方法提供实例,解释器会帮我们做这些的。
我们会发现,其实这两个方法,本质上就是在跟实例化这个问题较劲了。
类像一个门一样,门里的方法调用门里的变量,用self.变量名,门外的方法调用门外的变量,直接用变量名。门外的方法怎么调用门里的变量呢?门里的方法怎么调用门外的变量呢?
@classmethod
门外的方法调用门里的变量
把类封在方法里就好了。
def get_no_of_instances(cls):
return cls.no_inst
class Kls(object):
no_inst = 0
def __init__(self):
Kls.no_inst = Kls.no_inst + 1
print(get_no_of_instances(Kls))
很简单对不对?但是写在类外面不好看,毕竟是和类相关的方法,我们如何写在类里面呢?
class Kls(object):
no_inst = 0
def __init__(self):
Kls.no_inst = Kls.no_inst + 1
@classmethod
def get_no_of_instance(cls):
return cls.no_inst
def get_no_of_instance2(self):
return self.no_inst
ik1 = Kls()
print(ik1.get_no_of_instance())
print(Kls.get_no_of_instance())
print(ik1.get_no_of_instance2())
print(Kls.get_no_of_instance2(ik1))
print(Kls().get_no_of_instance2())
print(Kls.get_no_of_instance2(ik1))
#print(Kls.get_no_of_instance2())
on_inst表示了实例化的次数,运行代码你会发现,只有ik1=Kls()和倒数第三行Kls实例化了。
其实这就是类方法。不但写在类里面,调用的时候跟其他的方法保持一致,并且没有实例化,很漂亮对不对?颜值决定一切!
这里我补充了个get_no_of_instance2(self)方法,大家可以很容易发现正常调用应该怎么写,最后一行是仿写调用classmethod的,会报错。倒数第二行我们要传一个实例ik1。
接着我们说静态方法,你从文档的介绍来看,应该感觉跟类方法差不多的。
@staticmethod
门里的方法调用门外的变量
直接用嘛,干嘛那么麻烦。
IND = 'ON'
class Kls:
def __init__(self,data):
self.data = data
def do_reset(self):
if IND == 'ON':
print('Reset done for:', self.data)
def set_db(self):
if IND == 'ON':
self.db = 'new db connection'
print('DB connection made for:',self.data)
ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()
错了,不好意思,我们的问题不在这里,问题在于流畅的交互,从上面的例子可以看出来,不但可以直接使用,我们也可以在init中data的地方传进来,如果是变量,还好,如果是个function呢?
实际上静态方法应用的背景是:如果有一个function的内容跟class无关,也就是说不会调用class的任何方法,但是却又经常或者只被这个class调用,我可能会认为,他应该属于这个class(写在class里面),既然无关,self会显得多余啊!class外部还要调用这个方法。(比如log,或者error这样的)当然宗旨还是为了漂亮!
IND = 'ON'
class Kls:
def __init__(self, data):
self.data = data
@staticmethod
def checkind():
return (IND == 'ON')
def do_reset(self):
if self.checkind():
print('Reset done for:', self.data)
def set_db(self):
if self.checkind():
self.db = 'New db connection'
print('DB connection made for: ', self.data)
ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()
print(Kls.checkind())
代码写多了就会发现,归类的重要性。。。
最后,再做一组对比
class Kls(object):
def __init__(self, data):
self.data = data
def printd(self):
print(self.data)
@staticmethod
def smethod(*arg):
print('Static:', arg)
@classmethod
def cmethod(*arg):
print('Class:', *arg)
@classmethod
def cmethod(self, *arg):
print('Class:', self, *arg)
ik = Kls(23)
ik.printd()
ik.smethod()
ik.cmethod()
# Kls.printd()
Kls.smethod()
Kls.cmethod()