Python可調用對象__call__方法的用法分析

前言

最近有許多朋友私信問我,Python的可調用對象到底有什麼用處,爲什麼要費事的重載括號而不是直接綁定類的普通方法。下面就來爲大家分享__call__可調用對象的一些感悟。

精簡代碼,方便接口調用的“約定俗成”

class route(object):

    def __init__(self, res)
	    self.resource = res
    
    @classmethod
    def factory(cls):
        print 'factory'
        return cls()
    
    @webob.dec.wsgify
    def __call__(self,req):
        print 'route __call__'
        return self.resource()

class resource(object):

    @webob.dec.wsgify
    def __call__(self,req):
        print 'resource __call__'

class API(route):
    def __init__(self):
	    res = resource()
		super(API, self).__init__(res)
    
wsgi.server(eventlet.listen(('', 80)), API.factory())
上面的代碼是一個典型的WSGI服務的節選,如果不用__call__,那麼我們各組件之間可能要約定或規範一個接口,比如下面,大家都叫notcall()。。。

class route(object):

    def __init__(self, res)
	    self.resource = res
    
    @classmethod
    def factory(cls):
        print 'factory'
        return cls()
    
    @webob.dec.wsgify
    def notcall(self,req):
        print 'route notcall'
        return self.resource.notcall()

class resource(object):

    @webob.dec.wsgify
    def notcall(self,req):
        print 'resource notcall'

class API(route):
    def __init__(self):
	    res = resource()
		super(API, self).__init__(res)
    
wsgi.server(eventlet.listen(('', 80)), API.factory().notcall())
這樣用起來就非常麻煩,模塊之間合作要約定好接口的名字,編寫記憶許多接口文檔,增加代碼量且容易出錯。

只是想要函數,卻能完成不只是函數的工作

類似上面的代碼,許多模塊的接口的參數都是需要一個函數調用,比如這個wsgi.server(port, app),第二個參數就是一個實際的wsgi服務的函數調用。然後OOP大行其道的今天,貌似地球上乃至宇宙中的萬物都可被抽象成對象,然而在實際的coding中,我們真的需要將所有的東西都抽象成對象嗎?

這也是我喜歡Python的一個原因,雖然Python中萬物都是對象,但是卻提供這種對象可調用的方式,而它可以完成一些函數不能完成的工作。比如靜態變量,這在Python中是不允許的,但是通過__call__可以這樣做

class Factorial:
    def __init__(self):
        self.cache = {}
    def __call__(self, n):
        if n not in self.cache:
            if n == 0:
                self.cache[n] = 1
            else:
                self.cache[n] = n * self.__call__(n-1)
        return self.cache[n]

fact = Factorial()

for i in xrange(10):                                                             
    print("{}! = {}".format(i, fact(i)))

# output
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

對象綁定

在涉及新類對象綁定的時候,可以在元類放置對象綁定時的操作代碼

class test(type):
    pass

class test1(test):
    def __call__(self):
        print "I am in call"

class test2(object):
    __metaclass__=test1

t=test2()
#I am in call
test2是test1的實例。因爲test1是元類。在實例綁定元類的時候,__call__會調用


大家可以交流討論下,看看還有哪些設計模式可以應用於此,我會補充的



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章