程式扎記: [ Python 文章收集 ] Python可調用對象 __call__ 方法的用法分析

標籤

2015年5月11日 星期一

[ Python 文章收集 ] Python可調用對象 __call__ 方法的用法分析

Source From Here 
Preface 
Python 的可調用對像到底有什麼用處,為什麼要費事的重載括號而不是直接綁定類的普通方法。下面就來為大家分享__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.     def  __call__( self ,req):    
  11.         print  'route __call__'    
  12.         return  self.resource()    
  13.     
  14. class  resource(object):     
  15.     def  __call__( self ,req):    
  16.         print  'resource __call__'    
  17.     
  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.     def  notcall( self ,req):    
  11.         print  'route notcall'    
  12.         return  self .resource.notcall()    
  13.     
  14. class  resource(object):      
  15.     def  notcall( self ,req):    
  16.         print  'resource notcall'    
  17.     
  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.           
  5.     def  __call__( self , n):    
  6.         if  n  not  in  self .cache:    
  7.             if  n ==  0:    
  8.                 self .cache[n] =  1    
  9.             else:    
  10.                 self .cache[n] = n *  self .__call__(n-1)    
  11.         return  self .cache[n]    
  12.     
  13. fact = Factorial()    
  14.     
  15. for  i  in  xrange(10):                                                                 
  16.     print ( "{}! = {}" .format(i, fact(i)))   
執行結果: 
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

對象綁定 
在涉及新類對象綁定的時候,可以在元類放置對象綁定時的操作代碼: 
  1. class  Metaclass(type):    
  2.     pass    
  3.     
  4. class  Class1(Metaclass):    
  5.     def  __call__( self ):    
  6.         print  "I am in call: {}".format(self.__class__.__name__)    
  7.     
  8. class  Class2(object):    
  9.     __metaclass__=Class1    
  10.     
  11. t=Class2()    
  12. #I am in call: Class1  
Class2 是 Class1 的實例。因為 Class1 是元類。在實例綁定元類的時候,__call__ 會調用 


Supplement 
化整為零的次世代網頁開發標準: WSGI 
深刻理解 Python 中的元類 (metaclass)

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!