Python傳說中的函數(shù)神器functools模塊,為你全面解析

functools 模塊提供用于調(diào)整或擴(kuò)展函數(shù)和其他可調(diào)用對象的工具,而無需完全重寫它們。

裝飾器

partial 類是 functools 模塊提供的主要工具, 它可以用來“包裝”一個可調(diào)用的對象的默認(rèn)參數(shù)。它產(chǎn)生的對象本身是可調(diào)用的,可以看作是原生函數(shù)。它所有的參數(shù)都與原來的相同,并且可以使用額外的位置參數(shù)或命名參數(shù)來調(diào)用。使用 partial 代替 lambda 來為函數(shù)提供默認(rèn)參數(shù),同時保留那些未指定的參數(shù)。

本文福利:私信回復(fù)【PDF】可獲取Python電子書一套

Partial 對象

下面列子是對 myfunc 方法的兩個 partial 對象,show_details() 用于輸出partial對象的 func 、 args 和 keywords 屬性:

示例中最后調(diào)用第一個 partial 對象而沒有傳遞 a 的值,導(dǎo)致異常。

獲取函數(shù)屬性

默認(rèn)情況下, partial 對象沒有 __name__ 和 __doc__ 屬性。 這樣不利于被裝飾的函數(shù)進(jìn)行調(diào)試??梢允褂?update_wrapper() 從原函數(shù)復(fù)制或新增屬性到 partial 對象。

添加到裝飾器的屬性在 WRAPPER_ASSIGNMENTS 中定義,而 WRAPPER_UPDATES 列出要修改的值。

其他調(diào)用對象

partial適用于所有可調(diào)用可對象,并不是僅可用于獨(dú)立函數(shù)。

上面例子使用 MyClass 類的實(shí)例的 __call__() 方法創(chuàng)建了partial對象。照樣正常工作:

方法和函數(shù)

partial() 返回一個可以直接調(diào)用的對象, partialmethod() 返回一個可調(diào)用的為某個對象準(zhǔn)備的未綁定的方法。再下面例子中,同一個獨(dú)立函數(shù)被兩次添加到類 MyClass 屬性。使用 partialmethod() 生成 method1(), partial() 生成 method2():

method1() 可以被 MyClass 實(shí)例調(diào)用,和普通類方法一樣,實(shí)例作為第一個參數(shù)傳入。method2() 沒有被成功綁定為類方法。因此其 self 參數(shù)必須顯式傳入,所以此例拋出 TypeError 異常:

在裝飾器中使用

使用裝飾器時保持函數(shù)的屬性信息有時非常有用。但是使用裝飾器時難免會損失一些原本的功能信息。所以functools提供了 wraps() 裝飾器可以通過 update_wrapper() 將原函數(shù)對象的指定屬性復(fù)制給包裝函數(shù)對象。

比較

在Python2之前,類中可以定義 __cmp__() 方法,該方法根據(jù)對象是否小于、d等于或大于被比較項(xiàng)返回-1、0或1。Python2.1開始引入了?富比較?方法API(__lt__(), __le()__, __eq__(), __ne__(), __gt__() 和 __ge__()),用于執(zhí)行比較操作返回一個布爾值。Python3中 __cmp__() 放棄支持這些新方法,由 functools 提供工具,以便于編寫符合Python3中新的比較需求的類。

富比較

富比較API旨在允許具有復(fù)雜比較的類以最有效的方式實(shí)現(xiàn)每種計算。但是,對于比較相對簡單的類,手動創(chuàng)建每種富比較方法沒有意義。total_ordering() 類裝飾器可以使被裝飾的類只需要定義 __lt__(),__le__().__gt__()和__ge__() 中的其中一個和 __eq__(), 剩下的由該裝飾器自動提供。這簡化了定義所有富比較操作的工作量。

雖然該裝飾器能很容易的創(chuàng)建完全有序類型,但衍生出的比較函數(shù)執(zhí)行的可能會更慢,以及產(chǎn)生更復(fù)雜的堆棧跟蹤。如果性能基準(zhǔn)測試表明這是程序的瓶頸,則實(shí)現(xiàn)所有六個富比較函數(shù)可能會提高速度。

排序規(guī)則

在Python3中已經(jīng)廢棄了舊時的比較(cmp)函數(shù),因此例如 sorted(),min(),max()等方法不在支持 cmp參數(shù), 但仍然支持key函數(shù)。functools提供了 cmp_to_key() 用于將cmp函數(shù)轉(zhuǎn)換成key函數(shù)。

例如給定一個正整數(shù)列表,輸出用這些正整數(shù)能夠拼接成的最大整數(shù)。如果是Python2的程序可以是這樣:

但Python3的 sort 函數(shù)已廢棄 cmp 參數(shù),可以使用 cmp_to_key 將cmp函數(shù)轉(zhuǎn)換成key函數(shù):

cmp 函數(shù)接收兩個參數(shù),比較它們,如果小于返回負(fù)數(shù),相等返回0,大于返回正數(shù)。 key 函數(shù)接收一個參數(shù),返回用于排序的鍵。

緩存

lru_cache() 裝飾器是?緩存淘汰算法(最近最少使用)的一種實(shí)現(xiàn)。其使用函數(shù)的參數(shù)作為key結(jié)果作為value緩存在hash結(jié)構(gòu)中(因此函數(shù)的參數(shù)必須是hashable),如果后續(xù)使用相同參數(shù)再次調(diào)用將從hash從返回結(jié)果。同時裝飾器還添加了檢查緩存轉(zhuǎn)態(tài)方法(cache_info())和清空緩存方法(cache_clear())給函數(shù)。

代碼中多次調(diào)用 demo() 方法。首次調(diào)用后結(jié)果存在緩存中。cache_info() 返回一個命名元組,包括 hits,misses,maxsize 和 currsize 。當(dāng)?shù)诙握{(diào)用時命中緩存的調(diào)用將直接返回緩存內(nèi)容,cache_clear() 用于清空當(dāng)前緩存。

為了防止緩存在長時間運(yùn)行的流程中無限制地增長,特別設(shè)置了 maxsize 參數(shù), 默認(rèn)是128,設(shè)置為None時,則禁用LRU功能,緩存可以無限增長。同時還提供了 typed 參數(shù),用于設(shè)置是否區(qū)別參數(shù)類型,默認(rèn)為Fals。如果設(shè)置為True,那么類似如 demo(1) 和 demo(1.0) 將被視為不同的值不同的調(diào)用。

Reduce方法

Python3中取消了全局命名空間中的 reduce() 函數(shù),將 reduced() 放到了 functools 模塊中,要使用 reduce() 的話,要先從 functools 中加載。

函數(shù)重載

在動態(tài)類型的語言(如Python)中,如果需要根據(jù)參數(shù)的類型執(zhí)行不同的操作,簡單直接的方法就是檢查參數(shù)的類型。但在行為差異明顯的情況下需要分離成單獨(dú)的函數(shù)。 functools 提供 singledispatch() 裝飾器注冊一組通用函數(shù)基于函數(shù)的第一個參數(shù)的類型自動切換,類似于強(qiáng)類型語言中的函數(shù)重載。

被 singledispatch() 裝飾的函數(shù)是默認(rèn)實(shí)現(xiàn), 使用其 register() 屬性裝飾接收其他類型參數(shù)的函數(shù)。調(diào)用時會根據(jù) register() 中注冊的類型自動選擇實(shí)現(xiàn)函數(shù)。沒有則使用默認(rèn)實(shí)現(xiàn)。

另外再有繼承的情況下,當(dāng)類型沒有精確匹配時,將根據(jù)繼承順序,選擇最接近的類型。

在上面代碼中,類D和E沒有與任何已注冊的泛型函數(shù)匹配,所以根據(jù)其類的繼承順序進(jìn)行選擇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容