分享

Python可调用对象__call__方法的用法分析

坎蒂丝_Swan 发表于 2014-12-6 20:09:03 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 11928
本帖最后由 坎蒂丝_Swan 于 2014-12-6 20:10 编辑


问题导读

问题1:Python的可调用对象到底有什么用处?
问题2:为什么要费事的重载括号而不是直接绑定类的普通方法?







前言
今天就来为大家分享__call__可调用对象的一些感悟。

精简代码,方便接口调用的“约定俗成”

  1. class route(object):
  2.     def __init__(self, res)
  3.             self.resource = res
  4.    
  5.     @classmethod
  6.     def factory(cls):
  7.         print 'factory'
  8.         return cls()
  9.    
  10.     @webob.dec.wsgify
  11.     def __call__(self,req):
  12.         print 'route __call__'
  13.         return self.resource()
  14. class resource(object):
  15.     @webob.dec.wsgify
  16.     def __call__(self,req):
  17.         print 'resource __call__'
  18. class API(route):
  19.     def __init__(self):
  20.             res = resource()
  21.                 super(API, self).__init__(res)
  22.    
  23. wsgi.server(eventlet.listen(('', 80)), API.factory())
复制代码

上面的代码是一个典型的WSGI服务的节选,如果不用__call__,那么我们各组件之间可能要约定或规范一个接口,比如下面,大家都叫notcall()。。。
  1. class route(object):
  2.     def __init__(self, res)
  3.             self.resource = res
  4.    
  5.     @classmethod
  6.     def factory(cls):
  7.         print 'factory'
  8.         return cls()
  9.    
  10.     @webob.dec.wsgify
  11.     def notcall(self,req):
  12.         print 'route notcall'
  13.         return self.resource.notcall()
  14. class resource(object):
  15.     @webob.dec.wsgify
  16.     def notcall(self,req):
  17.         print 'resource notcall'
  18. class API(route):
  19.     def __init__(self):
  20.             res = resource()
  21.                 super(API, self).__init__(res)
  22.    
  23. wsgi.server(eventlet.listen(('', 80)), API.factory().notcall())
复制代码
这样用起来就非常麻烦,模块之间合作要约定好接口的名字,编写记忆许多接口文档,增加代码量且容易出错。


只是想要函数,却能完成不只是函数的工作

类似上面的代码,许多模块的接口的参数都是需要一个函数调用,比如这个wsgi.server(port, app),第二个参数就是一个实际的wsgi服务的函数调用。然后OOP大行其道的今天,貌似地球上乃至宇宙中的万物都可被抽象成对象,然而在实际的coding中,我们真的需要将所有的东西都抽象成对象吗?
这也是我喜欢Python的一个原因,虽然Python中万物都是对象,但是却提供这种对象可调用的方式,而它可以完成一些函数不能完成的工作。比如静态变量,这在Python中是不允许的,但是通过__call__可以这样做

  1. class Factorial:
  2.     def __init__(self):
  3.         self.cache = {}
  4.     def __call__(self, n):
  5.         if n not in self.cache:
  6.             if n == 0:
  7.                 self.cache[n] = 1
  8.             else:
  9.                 self.cache[n] = n * self.__call__(n-1)
  10.         return self.cache[n]
  11. fact = Factorial()
  12. for i in xrange(10):                                                            
  13.     print("{}! = {}".format(i, fact(i)))
  14. # output
  15. 0! = 1
  16. 1! = 1
  17. 2! = 2
  18. 3! = 6
  19. 4! = 24
  20. 5! = 120
  21. 6! = 720
  22. 7! = 5040
  23. 8! = 40320
  24. 9! = 362880
复制代码


对象绑定
在涉及新类对象绑定的时候,可以在元类放置对象绑定时的操作代码

  1. class test(type):
  2.     pass
  3. class test1(test):
  4.     def __call__(self):
  5.         print "I am in call"
  6. class test2(object):
  7.     __metaclass__=test1
  8. t=test2()
  9. #I am in call
复制代码
test2是test1的实例。因为test1是元类。在实例绑定元类的时候,__call__会调用。





欢迎加入about云群90371779322273151432264021 ,云计算爱好者群,亦可关注about云腾讯认证空间||关注本站微信

没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条