Python?Decorator的設(shè)計模式演繹過程解析
關(guān)于代理模式、裝飾模式
設(shè)計模式中經(jīng)常提到的代理模式、裝飾模式,這兩種叫法實際上是說的同一件事,只是側(cè)重點有所不同而已。
這兩者都是通過在原有對象的基礎(chǔ)上封裝一層對象,通過調(diào)用封裝后的對象而不是原來的對象來實現(xiàn)代理/裝飾的目的。
例如:(以Java為例)
public class CountProxy implements Count {
private CountImpl countImpl;
public CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@Override
public void queryCount() {
System.out.println("事務(wù)處理之前");
// 調(diào)用委托類的方法;
countImpl.queryCount();
System.out.println("事務(wù)處理之后");
}
@Override
public void updateCount() {
System.out.println("事務(wù)處理之前");
// 調(diào)用委托類的方法;
countImpl.updateCount();
System.out.println("事務(wù)處理之后");
}
}在這個例子中CountProxy是對CountImpl的封裝。
使用者通過CountProxy.queryCount方法來調(diào)用CountImpl.queryCount方法,這被稱為代理,即CountProxy是代理類,CountImpl是被代理類。
在CountProxy.queryCount方法中,可以在CountImpl.queryCount方法調(diào)用之前和之后添加一些額外的操作,被稱為裝飾,即CountProxy是裝飾類,CountImpl是被裝飾類。
如果強(qiáng)調(diào)通過CountProxy 對CountImpl進(jìn)行代理的作用,則稱為代理模式;
如果強(qiáng)調(diào)通過CountProxy 對CountImpl增加額外的操作,則稱為裝飾模式;
不論是哪種稱呼,其本質(zhì)都在于對原有對象的封裝。
其封裝的目的在于增強(qiáng)所封裝對象的功能或管理所封裝的對象。
從上面的例子也可以發(fā)現(xiàn),代理/封裝所圍繞的核心是可調(diào)用對象(比如函數(shù))。
Python中的代理/裝飾
Python中的可調(diào)用對象包括函數(shù)、方法、實現(xiàn)了__call__方法的類。
Python中的函數(shù)也是對象,可以作為高階函數(shù)的參數(shù)傳入或返回值返回。
因此,當(dāng)代理/裝飾的對象是函數(shù)時,可以使用高階函數(shù)來對某個函數(shù)進(jìn)行封裝。
例如:
def query_count_proxy(fun, name, age):
print('do something before')
rv = fun(name, age)
print('do something after')
return rv
def query_count(name, age):
print('name is %s, age is %d' % (name, age))
query_count_proxy(query_count, 'Lee', 20)但是,這個例子中,query_count函數(shù)作為參數(shù)傳入query_count_proxy函數(shù)中,并在query_count_proxy函數(shù)中被調(diào)用,其結(jié)果作為返回值返回。這就完成了代理的功能,同時,在調(diào)用query_count函數(shù)的前后,我們還增加了裝飾代碼。
但是,query_count_proxy的函數(shù)參數(shù)與query_count不一樣了,理想的代理應(yīng)該保持接口一致才對。
為了保持一致,我們可以利用高階函數(shù)可以返回函數(shù)的特點來完成:
def query_count_proxy(fun):
def wrapper(name, age):
print('do something before')
rv = fun(name, age)
print('do something after')
return rv
return wrapper
def query_count(name, age):
print('name is %s, age is %d' % (name, age))
query_count_proxy(query_count)('Lee', 20)修改后的例子,query_count_proxy僅負(fù)責(zé)接受被代理的函數(shù)query_count作為參數(shù),同時,返回一個函數(shù)對象wrapper作為返回值,真正的封裝動作在wrapper這個函數(shù)中完成。
此時,如果調(diào)用query_count_proxy(query_count)就得到了wrapper函數(shù)對象,則,執(zhí)行query_count_proxy(query_count)('Lee', 20)就相當(dāng)于執(zhí)行了wrapper('Lee', 20)。
但是可以看到,query_count_proxy(query_count)('Lee', 20)這種使用方法,仍然不能保證一致。
為了保持一致,我們需要利用Python中對象與其名稱可以動態(tài)綁定的特點。不使用query_count_proxy(quer_count)('Lee', 20)來調(diào)用代理函數(shù),而是使用下面兩句:
query_count = query_count_proxy(query_count)
query_count('Lee', 20)執(zhí)行query_count_proxy(query_count)生成wrapper函數(shù)對象,將這個對象通過query_count = query_count_proxy(query_count)綁定到query_count這個名字上來,這樣執(zhí)行query_count('Lee', 20)時,其實執(zhí)行的是wrapper('Lee', 20)。
這么做的結(jié)果就是:使用代理時調(diào)用query_count('Lee', 20)與不使用代理時調(diào)用query_count('Lee', 20)對使用者而言保持不變,不用改變代碼,但是在真正執(zhí)行時,使用的是代理/裝飾后的函數(shù)。
這里,基本利用Python的高階函數(shù)及名稱綁定完成了代理/裝飾的功能。
還有什么不理想的地方呢?
對,就是query_count = query_count_proxy(query_count),因為這句既不簡潔,又屬于重復(fù)工作。
Python為我們提供了語法糖來完成這類的tedious work。
方法就是:
@query_count_proxy
def query_count(name, age):
return 'name is %s, age is %d' % (name, age)query_count = query_count_proxy(query_count)就等同于在定義query_count函數(shù)的時候,在其前面加上@query_count_proxy。
Python看到這樣的語法,就會自動的執(zhí)行query_count = query_count_proxy(query_count)進(jìn)行name rebinding
補(bǔ)充
以上就是Python實現(xiàn)可調(diào)用對象裝飾的核心。
可調(diào)用對象包括函數(shù)、方法、實現(xiàn)了__call__方法的類,上述內(nèi)容只是針對函數(shù)來解釋,對于方法、實現(xiàn)了__call__方法的類,其基本原理相同,具體實現(xiàn)略有差別。
以上就是Python Decorator的設(shè)計模式演繹過程解析的詳細(xì)內(nèi)容,更多關(guān)于Python Decorator設(shè)計模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python的Scrapy框架中的CrawlSpider介紹和使用
這篇文章主要介紹了Python的Scrapy框架中的CrawlSpider介紹和使用,CrawlSpider其實是Spider的一個子類,除了繼承到Spider的特性和功能外,還派生除了其自己獨有的更加強(qiáng)大的特性和功能,其中最顯著的功能就是"LinkExtractors鏈接提取器",需要的朋友可以參考下2023-12-12
兩行Python代碼實現(xiàn)pdf轉(zhuǎn)word功能
這篇文章主要為大家詳細(xì)介紹了如何利用兩行Python代碼就能實現(xiàn)pdf轉(zhuǎn)word功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-03-03

