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

先来个总结:

定义:

绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

    1. 绑定到类的方法:用classmethod装饰器装饰的方法。

                为类量身定制

                类.boud_method(),自动将类当作第一个参数传入

              (其实对象也可调用,但仍将类当作第一个参数传入)

    2. 绑定到对象的方法:没有被任何装饰器装饰的方法。

               为对象量身定制

               对象.boud_method(),自动将对象当作第一个参数传入

             (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

非绑定方法:用staticmethod装饰器装饰的方法

         不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已

    注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而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 0x000001E596E41A90>
A.m2(1) #cls: <class '__main__.A'>
A.m3(1)

在类中一共定义了3个方法,

    m1 是实例方法,第一个参数必须是 self(约定俗成的);

    m2 是类方法,第一个参数必须是cls(同样是约定俗成);

    m3 是静态方法,参数根据业务需求定,可有可无。

当程序运行时,大概发生了这么几件事(结合下面的图来看)。

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

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

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

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

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

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

   实例方法 :

     print(A.m1)

print(A.m1)>>:
# A.m1在py2中显示为<unbound method A.m1>
<function A.m1 at 0x000002BF7FF9A488> print(a.m1)>>:
<bound method A.m1 of <__main__.A object at 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() >>:
TypeError: m1() missing 1 required positional argument: 'n'
#类方法 print(A.m2) >>:
<bound method A.m2 of <class '__main__.A'>>
print(a.m2) >>:
<bound method A.m2 of <class '__main__.A'>>
#加了@classmethod,都自动绑定了类对象A的方法

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)

@staticmethod静态方法

print(A.m3) >>:
<function A.m3 at 0x000002BF7FF9A840>
print(a.m3) >>:
<function A.m3 at 0x000002BF7FF9A840>

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

A.m3(1) == a.m3(1)
# 以上等式左右两边的意义一样

静态方法的使用场景: 
  如果在方法中不需要访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,往往这种方法放在类外面的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务。例如下面是微信公众号开发中验证微信签名的一个例子,它没有引用任何类或者实例相关的属性和方法。

from hashlib import sha1
import tornado.web
class SignatureHandler(tornado.web.RequestHandler):
def get(self):
"""
根据签名判断请求是否来自微信
"""
signature = self.get_query_argument("signature", None)
echostr = self.get_query_argument("echostr", None)
timestamp = self.get_query_argument("timestamp", None)
nonce = self.get_query_argument("nonce", None)
if self._check_sign(TOKEN, timestamp, nonce, signature):
logger.info("微信签名校验成功")
self.write(echostr)
else:
self.write("你不是微信发过来的请求")
@staticmethod
def _check_sign(token, timestamp, nonce, signature):
sign = [token, timestamp, nonce]
sign.sort()
sign = "".join(sign)
sign = sha1(sign).hexdigest()
return sign == signature

类方法的使用场景有:

  作为工厂方法创建实例对象,例如内置模块 datetime.date 类中就有大量使用类方法作为工厂方法,以此来创建date对象。

class date:
def __new__(cls, year, month=None, day=None):
self = object.__new__(cls)
self._year = year
self._month = month
self._day = day
return self @classmethod
def fromtimestamp(cls, t):
y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
return cls(y, m, d)
@classmethod
def today(cls):
t = _time.time()
return cls.fromtimestamp(t)

如果希望在方法里面调用静态类,那么把方法定义成类方法是合适的,因为要是定义成静态方法,那么你就要显示地引用类A,这对继承来说可不是一件好事情。

class A: 

    @staticmethod
def m1()
pass @staticmethod
def m2():
A.m1() # bad @classmethod
def m3(cls):
cls.m1() # good

本篇参考:Python之禅公共号文章

Python学习笔记之@classmethod与@staticmethod的更多相关文章

  1. 【python学习笔记】9.魔法方法、属性和迭代器

    [python学习笔记]9.魔法方法.属性和迭代器 魔法方法:xx, 收尾各有两个下划线的方法 __init__(self): 构造方法,创建对象时候自动执行,可以为其增加参数, 父类构造方法不会被自 ...

  2. Python 学习笔记(下)

    Python 学习笔记(下) 这份笔记是我在系统地学习python时记录的,它不能算是一份完整的参考,但里面大都是我觉得比较重要的地方. 目录 Python 学习笔记(下) 函数设计与使用 形参与实参 ...

  3. python学习笔记整理——字典

    python学习笔记整理 数据结构--字典 无序的 {键:值} 对集合 用于查询的方法 len(d) Return the number of items in the dictionary d. 返 ...

  4. VS2013中Python学习笔记[Django Web的第一个网页]

    前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...

  5. python学习笔记之module && package

    个人总结: import module,module就是文件名,导入那个python文件 import package,package就是一个文件夹,导入的文件夹下有一个__init__.py的文件, ...

  6. python学习笔记(六)文件夹遍历,异常处理

    python学习笔记(六) 文件夹遍历 1.递归遍历 import os allfile = [] def dirList(path): filelist = os.listdir(path) for ...

  7. python学习笔记--Django入门四 管理站点--二

    接上一节  python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...

  8. python学习笔记--Django入门0 安装dangjo

    经过这几天的折腾,经历了Django的各种报错,翻译的内容虽然不错,但是与实际的版本有差别,会出现各种奇葩的错误.现在终于找到了解决方法:查看英文原版内容:http://djangobook.com/ ...

  9. python学习笔记(一)元组,序列,字典

    python学习笔记(一)元组,序列,字典

随机推荐

  1. 2018牛客网暑期ACM多校训练营(第五场) E - room - [最小费用最大流模板题]

    题目链接:https://www.nowcoder.com/acm/contest/143/E 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K ...

  2. codeforces 883H - Palindromic Cut - [字符串处理]

    题目链接:http://codeforces.com/problemset/problem/883/H Time limit: 3000 ms Memory limit: 262144 kB Koly ...

  3. Python 核实文件是否存在的函数

    经常会用到的文件读取,首先要检测文件的状态 # 核实文件是否存在 # Python2.6 开始,新增了一种格式化字符串的函数 str.format(),它增强了字符串格式化的功能. # format ...

  4. docker daemon configuration

    于 Docker的分层镜像,除了 aufs,docker还支持btrfs, devicemapper和vfs,你可以使用 -s 或 –storage-driver= 选项来指定相关的镜像存储.在Ubu ...

  5. yum provides "*/nmcli" and apt-get

    一般来说著名的linux系统基本上分两大类: 1.RedHat系列:Redhat.Centos.Fedora等 2.Debian系列:Debian.Ubuntu等 RedHat 系列 1 常见的安装包 ...

  6. MYSQL 命令行显示乱码 解决方案

    中文乱码是因为编码集不支持,所以要改变编码 先查看下设置的编码 使用如下命令 show variables like 'character%'; 在 mysql.conf (Ubuntu mysql5 ...

  7. Effective Java 目录

    <Effective Java>目录摘抄. 我知道这看起来很糟糕.当下,自己缺少实际操作,只能暂时摘抄下目录.随着,实践的增多,慢慢填充更多的示例. Chapter 2 Creating ...

  8. vue学习之六路由系统

    一.vueRouter实现原理 VueRouter的实现原理是根据监控锚点值的改变,从而不断修改组件内容来实现的,我们来试试不使用VueRouter,自己实现路由控制,如下代码: <!DOCTY ...

  9. [py][mx]django get方法返回login页面

    get方法返回login.html users/views.py def login(request): if request.method == "POST": pass eli ...

  10. POJ1258:Agri-Net(最小生成树模板题)

    http://poj.org/problem?id=1258 Description Farmer John has been elected mayor of his town! One of hi ...