Chinaunix首页 | 论坛 | 博客
  • 博客访问: 158875
  • 博文数量: 45
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 273
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-28 10:30
文章分类
文章存档

2017年(6)

2016年(3)

2015年(8)

2014年(28)

我的朋友

分类: Python/Ruby

2014-07-19 22:11:31

理解python的staticmethod与classmethod实现

    来源:http://luozhaoyu.iteye.com/blog/1506376
本文源于一时好奇,想要弄清出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到底是个什么东东?
Python代码  收藏代码
  1. @staticmethod  
  2. def foo(x):  
  3.     print(x)  


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

研究了下官方的代码,我再改了改,感觉应该这样写:
Python代码  收藏代码
  1. def foo(x):  
  2.     print(x)  
  3. class StaticMethod(object):  
  4.     def __init__(self, function):  
  5.         print("__init__() called")  
  6.         self.f = function  
  7.     def __get__(self, instance, owner):  
  8.         print("\t__get__() called")  
  9.         print("\tINFO: self = %s, instance =%s, owner = %s" % (self, instance, owner))  
  10.         return self.f  
  11.   
  12. class Class1(object):  
  13.     method = StaticMethod(foo)  
  14.       
  15. if __name__ == '__main__':  
  16.     ins = Class1()  
  17.     print("ins = %s, Class1 = %s" % (ins, Class1))  
  18.     print("ins.method = %s, Class1.method = %s" % (ins.method, Class1.method))  
  19.     ins.method('abc')  
  20.     Class1.method('xyz')  


输出结果是:
Python代码  收藏代码
  1. __init__() called  
  2. ins = <__main__.Class1 object at 0xece2d0>, Class1 = <class '__main__.Class1'>  
  3.     __get__() called  
  4.     INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =<__main__.Class1 object at 0xece2d0>, owner = <class '__main__.Class1'>  
  5.     __get__() called  
  6.     INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =None, owner = <class '__main__.Class1'>  
  7. ins.method = 0xeb6c00>, Class1.method = 0xeb6c00>  
  8.     __get__() called  
  9.     INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =<__main__.Class1 object at 0xece2d0>, owner = <class '__main__.Class1'>  
  10. abc  
  11.     __get__() called  
  12.     INFO: self = <__main__.StaticMethod object at 0xece5d0>, instance =None, owner = <class '__main__.Class1'>  
  13. xyz  

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

好的,那么classmethod又如何实现呢?
Python代码  收藏代码
  1. def foo2(cls, x):  
  2.     print("foo2's class = "cls)  
  3.     print(x)  
  4.   
  5. class ClassMethod(object):  
  6.     def __init__(self, function):  
  7.         print("ClassMethod: __init__() called")  
  8.         self.f = function  
  9.     def __get__(self, instance, owner = None):  
  10.         print("\t__get__() called")  
  11.         print("\tINFO: self = %s, instance =%s, owner = %s" % (self, instance, owner))  
  12.         def tmpfunc(x):  
  13.             print("I'm tmpfunc")  
  14.             return self.f(owner, x)  
  15.         return tmpfunc  
  16.   
  17. class Class2(object):  
  18.     method = ClassMethod(foo2)  
  19.   
  20. class Class21(Class2):  
  21.     pass  
  22. if __name__ == '__main__':  
  23.     ins = Class2()  
  24.     print("ins.method = %s, Class2.method = %s, Class21.method = %s" % (ins.method, Class2.method, Class21.method))  
  25.     ins.method('abc')  
  26.     Class2.method('xyz')  
  27.     Class21.method('asdf')  


输出结果是:
Python代码  收藏代码
  1. ClassMethod: __init__() called  
  2.     __get__() called  
  3.     INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =<__main__.Class2 object at 0xdeb350>, owner = <class '__main__.Class2'>  
  4.     __get__() called  
  5.     INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class2'>  
  6.     __get__() called  
  7.     INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class21'>  
  8. ins.method = 0xdee050>, Class2.method = 0xdee1e8>, Class21.method = 0xdee270>  
  9.     __get__() called  
  10.     INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =<__main__.Class2 object at 0xdeb350>, owner = <class '__main__.Class2'>  
  11. I'm tmpfunc  
  12. foo2's class =  __main__.Class2'>  
  13. abc  
  14.     __get__() called  
  15.     INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class2'>  
  16. I'm tmpfunc  
  17. foo2's class =  __main__.Class2'>  
  18. xyz  
  19.     __get__() called  
  20.     INFO: self = <__main__.ClassMethod object at 0xdeb250>, instance =None, owner = <class '__main__.Class21'>  
  21. I'm tmpfunc  
  22. foo2's class =  __main__.Class21'>  
  23. asdf  


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

有个地方值得注意,可能同志们刚才也已经想到了,我一定必须要定义一个tempfunc,再返回它才能完成工作吗?可不可以不要
Java代码  收藏代码
  1. def tmpfunc(x):  
  2.             print("I'm tmpfunc")  
  3.             return self.f(owner, x)  
  4.         return tmpfunc  


而直接返回一个
Java代码  收藏代码
  1. return self.f(owner, *args)  



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

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

小结:看来staticmethod和classmethod实现不是很困难,多亏了__get__函数帮忙。前文也提到__get__被调用时 会把instance和class信息都填进来,真是帮了很大忙。但是,这个__get__函数到底又是怎么一回事?为什么这么神奇?这里卖个关子,本文 先不讲了,下篇博文再看看这个__get__函数
阅读(1015) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~