python的staticmethod与classmethod实现实例代码


Posted in Python onFebruary 11, 2018

本文源于一时好奇,想要弄清出python的staticmethod()这一builtin方法的实现,查了一些资料(主要是python官方手册了)汇集于此

python在类中,有三种调用method的方法:普通method,staticmethod和classmethod
前两个应该都好理解,classmethod就是在调用这个函数的时候,会把调用对象的class object对象隐式地传进去。咦?这个class object不是一个类型?No,在python里面,class object不像静态语言一样是个类型,它在虚拟机中,就是一个对象。普通method调用需要把自己self作为参数传递,初学的时候怎么着也不能理解,不过看多了就自然熟悉了。比较奇怪的是staticmethod和classmethod不像静态语言一样,通过保留关键字定义,而是使用@staticmethod或者staticmethod()这种builtin函数进行定义。这个@staticmethod到底是个什么东东?

@staticmethod 
def foo(x): 
 print(x)

之前用过java,所以第一反应这是个annotation……唔,确实感觉像个AOP的东西,python里把它称作decorator。如果我们要自己实现一个staticmethod,该怎么写呢?

研究了下官方的代码,我再改了改,感觉应该这样写:

def foo(x): 
 print(x) 
class StaticMethod(object): 
 def __init__(self, function): 
  print("__init__() called") 
  self.f = function 
 def __get__(self, instance, owner): 
  print("\t__get__() called") 
  print("\tINFO: self = %s, instance =%s, owner = %s" % (self, instance, owner)) 
  return self.f 
 
class Class1(object): 
 method = StaticMethod(foo) 
  
if __name__ == '__main__': 
 ins = Class1() 
 print("ins = %s, Class1 = %s" % (ins, Class1)) 
 print("ins.method = %s, Class1.method = %s" % (ins.method, Class1.method)) 
 ins.method('abc') 
 Class1.method('xyz')

输出结果是:

__init__() called
ins = <__main__.Class1 object at 0xece2d0>, Class1 = <class '__main__.Class1'>
__get__() called
INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =<__main__.Class1 object at 0xece2d0>, owner = <class '__main__.Class1'>
__get__() called
INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =None, owner = <class '__main__.Class1'>
ins.method = <function foo at 0xeb6c00>, Class1.method = <function foo at 0xeb6c00>
__get__() called
INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =<__main__.Class1 object at 0xece2d0>, owner = <class '__main__.Class1'>
abc
__get__() called
INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =None, owner = <class '__main__.Class1'>
xyz

嗯,看上去一切都挺顺利,Class1包含了一个变量method,不过这个method其实也是一个特殊处理过的StaticMethod类。这个类中有一个__get__函数,当类被“get”的时候,被访问的时候,会默认把访问者的instance和class信息都传进来。所以我们看到不管是否调用method()这个函数,只要碰着了method,这个函数就会触发,就会打印出当前instance和class信息。虽然ins和Class1的instance各有不同,但__get__函数中只是返回foo函数,所以这里调用method之时就没有区别,调用的都是同一个function对象。

好的,那么classmethod又如何实现呢?

def foo2(cls, x): 
 print("foo2's class = ", cls) 
 print(x) 
 
class ClassMethod(object): 
 def __init__(self, function): 
  print("ClassMethod: __init__() called") 
  self.f = function 
 def __get__(self, instance, owner = None): 
  print("\t__get__() called") 
  print("\tINFO: self = %s, instance =%s, owner = %s" % (self, instance, owner)) 
  def tmpfunc(x): 
   print("I'm tmpfunc") 
   return self.f(owner, x) 
  return tmpfunc 
 
class Class2(object): 
 method = ClassMethod(foo2) 
 
class Class21(Class2): 
 pass 
if __name__ == '__main__': 
 ins = Class2() 
 print("ins.method = %s, Class2.method = %s, Class21.method = %s" % (ins.method, Class2.method, Class21.method)) 
 ins.method('abc') 
 Class2.method('xyz') 
 Class21.method('asdf')

输出结果是:

ClassMethod: __init__() called
__get__() called
INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =<__main__.Class2 object at 0xdeb350>, owner = <class '__main__.Class2'>
__get__() called
INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class2'>
__get__() called
INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class21'>
ins.method = <function tmpfunc at 0xdee050>, Class2.method = <function tmpfunc at 0xdee1e8>, Class21.method = <function tmpfunc at 0xdee270>
__get__() called
INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =<__main__.Class2 object at 0xdeb350>, owner = <class '__main__.Class2'>
I'm tmpfunc
foo2's class = <class '__main__.Class2'>
abc
__get__() called
INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class2'>
I'm tmpfunc
foo2's class = <class '__main__.Class2'>
xyz
__get__() called
INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class21'>
I'm tmpfunc
foo2's class = <class '__main__.Class21'>
asdf

可以看出,classmethod和staticmethod的实现方法是大同小异。staticmethod比较简单,直接返回self.f变量就好了,而classmethod不行,需要把调用时候的class类型信息传给foo2函数,这个函数根据接收的class信息来作不同的工作。(不过我现在也没有想到可以用来做些什么)

有个地方值得注意,可能同志们刚才也已经想到了,我一定必须要定义一个tempfunc,再返回它才能完成工作吗?可不可以不要

def tmpfunc(x): 
   print("I'm tmpfunc") 
   return self.f(owner, x) 
  return tmpfunc

而直接返回一个

return self.f(owner, *args)

我刚试了一把,直接传args默认参数是不行的,因为__get__被调用的时候,还没有把参数传进来。只有return tmpfunc之后,Class2.method('xyz')的参数才挂在tmpfunc之上。

当然,如果有朋友成功做到了,请一定留言告诉我XD

小结:看来staticmethod和classmethod实现不是很困难,多亏了__get__函数帮忙。前文也提到__get__被调用时会把instance和class信息都填进来,真是帮了很大忙。但是,这个__get__函数到底又是怎么一回事?为什么这么神奇?大家可以参考Python中 __get__和__getattr__和__getattribute__的区别

总结

以上就是本文关于python的staticmethod与classmethod实现实例代码的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

Python 相关文章推荐
忘记ftp密码使用python ftplib库暴力破解密码的方法示例
Jan 22 Python
Python随机数用法实例详解【基于random模块】
Apr 18 Python
python中如何使用正则表达式的非贪婪模式示例
Oct 09 Python
浅谈python配置与使用OpenCV踩的一些坑
Apr 02 Python
如何用Python实现简单的Markdown转换器
Jul 16 Python
python 实现图片旋转 上下左右 180度旋转的示例
Jan 24 Python
Django中ORM外键和表的关系详解
May 20 Python
Python Django 实现简单注册功能过程详解
Jul 29 Python
python 字典有序并写入json文件过程解析
Sep 30 Python
关于Python 中的时间处理包datetime和arrow的方法详解
Mar 19 Python
django实现更改数据库某个字段以及字段段内数据
Mar 31 Python
详解Pandas 处理缺失值指令大全
Jul 30 Python
Python语言的变量认识及操作方法
Feb 11 #Python
利用Opencv中Houghline方法实现直线检测
Feb 11 #Python
tensorflow输出权重值和偏差的方法
Feb 10 #Python
详解tensorflow实现迁移学习实例
Feb 10 #Python
Python学习之Django的管理界面代码示例
Feb 10 #Python
Tensorflow 自带可视化Tensorboard使用方法(附项目代码)
Feb 10 #Python
tensorflow训练中出现nan问题的解决
Feb 10 #Python
You might like
操作Oracle的php类
2006/10/09 PHP
PHP读取大文件的几种方法介绍
2016/10/27 PHP
thinkPHP实现的省市区三级联动功能示例
2017/05/05 PHP
PHP的RSA加密解密方法以及开发接口使用
2018/02/11 PHP
javascript 面向对象全新理练之数据的封装
2009/12/03 Javascript
JqGrid web打印实现代码
2011/05/31 Javascript
通过JS获取用户本地图片路径并显示的代码
2012/02/16 Javascript
JS字符串累加Array不一定比字符串累加快(根据电脑配置)
2012/05/14 Javascript
20款非常优秀的 jQuery 工具提示插件 推荐
2012/07/15 Javascript
js获得页面的高度和宽度的方法
2014/02/23 Javascript
jQuery实现的输入框选择时间插件用法实例
2015/02/28 Javascript
浅析Angular2子模块以及异步加载
2017/04/24 Javascript
JavaScript正则表达式校验与递归函数实际应用实例解析
2017/08/04 Javascript
Vue 报错TypeError: this.$set is not a function 的解决方法
2018/12/17 Javascript
Vue.js暴露方法给WebView的使用操作
2020/09/07 Javascript
JQuery+drag.js上传图片并且实现图片拖曳
2020/11/18 jQuery
[02:55]2018DOTA2国际邀请赛勇士令状不朽珍藏Ⅲ饰品一览
2018/08/01 DOTA
python自然语言编码转换模块codecs介绍
2015/04/08 Python
Python 2与Python 3版本和编码的对比
2017/02/14 Python
浅谈python和C语言混编的几种方式(推荐)
2017/09/27 Python
用python 批量更改图像尺寸到统一大小的方法
2018/03/31 Python
使用python创建生成动态链接库dll的方法
2020/05/09 Python
jupyter notebook的安装与使用详解
2020/05/18 Python
地球上最先进的胡子和头发修剪器:Bevel
2018/01/23 全球购物
实习教师自我鉴定
2013/09/27 职场文书
单位活动策划方案
2014/08/17 职场文书
解除施工合同协议书
2014/10/17 职场文书
幼儿园辞职书
2015/02/26 职场文书
2015社区精神文明建设工作总结
2015/04/21 职场文书
2015年度女工工作总结
2015/10/22 职场文书
初中化学教学反思
2016/02/22 职场文书
2019公司管理制度
2019/04/19 职场文书
如何获取numpy array前N个最大值
2021/05/14 Python
pytorch实现ResNet结构的实例代码
2021/05/17 Python
Pytorch中Softmax和LogSoftmax的使用详解
2021/06/05 Python
Win11任务栏太宽了怎么办?一招解决Win11任务栏太宽问题
2021/11/21 数码科技