专栏名称: Python之禅
分享Python相关技术干货,偶尔扯扯其它的
目录
相关文章推荐
小小的python学习社  ·  Python100道真题题库 ·  8 小时前  
Python爱好者社区  ·  DeepSeek满血版,来了! ·  3 天前  
Python爱好者社区  ·  DeepSeek 被放弃了,阿里牛逼! ·  2 天前  
Python爱好者社区  ·  刚刚,DeepSeek开源MoE训练、推理E ... ·  2 天前  
Python开发者  ·  清北 DeepSeek ... ·  3 天前  
51好读  ›  专栏  ›  Python之禅

@classmethod与@staticmethod的区别

Python之禅  · 公众号  · Python  · 2017-07-18 18:23

正文

题图:https://www.behance.net/AdamQuest

Python面向对象编程中,类中定义的方法可以是 @classmethod 装饰的 类方法 ,也可以是 @staticmethod 装饰的 静态方法 ,用的最多的还是不带装饰器的 实例方法 ,如果把这几个方法放一块,对初学者来说无疑是一头雾水,那我们该如何正确地使用它们呢?

先来看一个简单示例:

class A(object):
   def m1(self, n):
       print("self:", self)

   @classmethod
   def m2(cls, n):
       print("cls:", cls)

   @staticmethod
   def m3(n):
       pass

a = A()
a.m1(1) # self: <__main__.a object="" at=""/>
A.m2(1) # cls:
A.m3(1)

我在类中一共定义了3个方法,m1 是实例方法,第一个参数必须是 self(约定俗成的)。m2 是类方法,第一个参数必须是cls(同样是约定俗成),m3 是静态方法,参数根据业务需求定,可有可无。当程序运行时,大概发生了这么几件事(结合下面的图来看)。

  • 第一步:代码从第一行开始执行 class 命令,此时会创建一个类 A 对象(没错,类也是对象,一切皆对象嘛)同时初始化类里面的属性和方法,记住,此刻实例对象还没创建出来。

  • 第二、三步:接着执行 a=A() ,系统自动调用类的构造器,构造出实例对象 a

  • 第四步:接着调用 a.m1(1) ,m1 是实例方法,内部会自动把实例对象传递给 self 参数进行绑定,也就是说, self 和 a 指向的都是同一个实例对象。

  • 第五步:调用 A.m2(1) 时,python内部隐式地把类对象传递给 cls 参数,cls 和 A 都指向类对象。

严格意义上来说,左边的都是变量名,是对象的引用,右边才是真正的对像,为了描述方便,我直接把 a 称为对象,你应该明白我说对象其实是它所引用右边的那个真正的对象。

再来看看每个方法各有什么特性

实例方法

print(A.m1)
# A.m1在py2中显示为
0x000002BF7FF9A488>

print(a.m1)
0x000002BF7FFA2BE0>>

A.m1是一个还没有绑定实例对象的方法,对于未绑定方法,调用 A.m1 时必须显示地传入一个实例对象进去,而 a.m1是已经绑定了实例的方法,python隐式地把对象传递给了self参数,所以不再手动传递参数,这是调用实例方法的过程。

A.m1(a, 1)
# 等价  
a.m1(1)

如果未绑定的方法 A.m1 不传实例对象给 self 时,就会报参数缺失错误,在 py3 与 py2 中,两者报的错误不一致,python2 要求第一个参数self是实例对象,而python3中可以是任意对象。

A.m1(1)
TypeError: m1() missing 1 required positional argument: 'n'

类方法

print(A.m2)
>

print(a.m2)
>

m2是类方法,不管是 A.m2 还是 a.m2,都是已经自动绑定了类对象A的方法,对于后者,因为python可以通过实例对象a找到它所属的类是A,找到A之后自动绑定到 cls。

 A.m2(1) 
# 等价
a.m2(1)

这使得我们可以在实例方法中通过使用 self.m2()这种方式来调用类方法和静态方法。

def m1(self, n):
   print("self:", self)
   self.m2(n)

静态方法

print(A.m3)
0x000002BF7FF9A840>

print(a.m3)
0x000002BF7FF9A840>

m3是类里面的一个静态方法,跟普通函数没什么区别,与类和实例都没有所谓的绑定关系,它只不过是碰巧存在类中的一个函数而已。不论是通过类还是实例都可以引用该方法。

 A.m3(1) 
# 等价
a.m3(1)

以上就是几个方法的基本介绍。现在把几个基本的概念理清楚了,那么现在来说说几个方法之间的使用场景以及他们之间的优缺点。

应用场景

静态方法的使用场景:

如果在方法中不需要访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,往往这种方法放在类外面的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务。

例如下面是微信公众号开发中验证微信签名的一个例子,它没有引用任何类或者实例相关的属性和方法。

from hashlib import sha1
import tornado.web

class SignatureHandler(tornado.web.RequestHandler):
   def get(self):
       """
        根据签名判断请求是否来自微信
       """

       if self._check_sign(TOKEN, timestamp, nonce, signature):
           self.write(echostr)
       else:
           self.write("你不是微信发过来的请求"






请到「今天看啥」查看全文