Python面向对象编程,类中定义的方法可以是@classmethod修饰的类方法,也可以是@staticmethod修饰的静态方法,用的最多的还是没有decorator实例方法,如果把这些方法放在一起,对于初学者来说无疑是一头雾水,那么我们应该如何正确使用呢?我们先看一个简单的例子:):passa=A()a.m1(1)#self:<__main__.Aobjectat0x000001E596E41A90>A.m2(1)#cls:A.m3(1)我在中定义了3个方法类,m1是一个实例方法,第一个参数必须是self(按照惯例)。m2是类方法,第一个参数必须是cls(也是约定俗成),m3是静态方法,参数根据业务需要确定,可选。程序运行的时候,大概会发生几件事情(结合下图)。第一步:代码从第一行开始执行类命令。这时候会创建一个A类的对象(是的,类也是对象,万物皆对象)。同时初始化类中的属性和方法。记住,此时实例对象还没有被创建。第二步和第三步:然后执行a=A(),系统自动调用类的构造函数构造实例对象a第四步:然后调用a.m1(1),m1为实例方法,instance会在内部自动转换成对象传递给self参数进行绑定,即self和a都指向同一个实例对象。第五步:在调用A.m2(1)时,python将类对象隐式传递给cls参数,cls和A都指向类对象。严格来说,左边是变量名,是对象的引用,右边是真正的对象。为了描述方便,我直接将a称为对象。你应该明白,我说的对象其实就是它所指的右边的那个。那个真实的物体。让我们来看看每种方法的特点。实例方法print(A.m1)#A.m1在py2中显示为print(a.m1)>A.m1尚未绑定实例对象的方法,对于未绑定的方法,调用a.m1时必须显式传入一个实例对象,而a.m1是已经绑定实例的方法,python隐式将对象传递给self参数,所以不再手动传递参数,这是调用实例方法的过程。A.m1(a,1)#equivalenttoa.m1(1)如果unbound方法A.m1没有给self传递一个实例对象,就会报参数丢失错误。在py3和py2中,都报错不一致,python2要求第一个参数self是实例对象,而在python3中可以是任意对象。A.m1(1)TypeError:m1()missing1requiredpositionalargument:'n'classmethodprint(A.m2)>print(a.m2)>m2是类方法,无论是A.m2还是a.m2,都是已经自动绑定到类对象A的方法。对于后者,因为python可以找到它所属的类通过实例对象a是A,找到A后自动绑定clsA.m2(1)#equivalenta.m2(1)这样我们就可以通过在self.m2()中调用类方法和静态方法实例方法。defm1(self,n):print("self:",self)self.m2(n)静态方法print(A.m3)print(a.m3)m3在里面class的一个静态方法,它和普通的函数没有什么区别,与类和实例没有所谓的绑定关系,它只是一个恰好存在于类中的函数。可以通过类或实例引用此方法。a.m3(1)#equivalenta.m3(1)以上是几个方法的基本介绍。弄清了几个基本概念之后,我们就来说说几种方法之间的使用场景和优缺点。ApplicationScenario静态方法的使用场景:如果不需要访问方法中的任何实例方法和属性,纯粹是一个传入参数返回数据的函数式方法,那么适合用static来定义方法,节省了实例化对象的开销成本,这个方法作为一个函数存在于类之外的模块层往往是没有问题的,放在类中只为本类服务。比如下面是微信开发中的微信签名验证的例子公众号,没有引用任何类或者实例相关的属性和方法。fromhashlibimportsha1importtornado.webclassSignatureHandler(tornado.web.RequestHandler):defget(self):"""根据签名判断请求是否来自微信"""ifself._check_sign(TOKEN,timestamp,nonce,signature):self.write(echostr)else:self.write("你不是微信的请求")@staticmethoddef_check_sign(token,timestamp,nonce,signature):sign=[token,timestamp,nonce]sign.sort()sign="".join(sign)sign=sha1(sign).hexdigest()returnsign==signature类方法用于以下场景:作为创建实例对象的工厂方法,例如内置模块datetime.date类中,有大量的类方法用作工厂方法来创建日期对象。classdate:def__new__(cls,year,month=None,day=None):self=object.__new__(cls)self._year=yearself._month=monthself._day=dayreturnsself@classmethoddeffromtimestamp(cls,t):y,m,d,*=_time.localtime(t)returncls(y,m,d)@classmethoddeftoday(cls):t=_time.time()returncls.fromtimestamp(t)如果要在方法中调用静态类,那么definethemethod类的方法是合适的,因为如果定义为静态方法,那么你就得显式引用A类,这对继承来说可不是什么好事。classA:@staticmethoddefm1()pass@staticmethoddefm2():A.m1()#bad@classmethoddefm3(cls):cls.m1()#good其实也算不上深入理解,顶多是了解怎么用,真的要深入下去恐怕要另写一篇文章来了解了。如果有兴趣,可以了解一下Python的描述符。【本文为专栏作家“刘志军”原创文章,作者微信公众号:Python之禅(VTtalk)】点此阅读更多本作者好文