Python--偏函數(Partial)


出處  https://blog.csdn.net/Appleyk/article/details/77609114

一、什么是偏函數?

(1)在Python的functools模塊眾多的功能中,其中有一個就是偏函數,我們稱之為 partial function

         模塊的概念我們下一篇在細講。

(2)我們都聽過偏將軍吧,在三國時代的官制中,系將軍的輔佐,與裨將軍兩者都為雜號將軍;今天我們要講的偏函數,其實是函數的輔佐,什么意思呢,我們借助Python的help幫助函數,看一下:

這里我們主要說下紅色圈的意思:

partial 一共有三個部分:

(1)第一部分也就是第一個參數,是一個函數,這個函數可以是你定義的,也可以是Python內置函數

(2)第二部分是一個可變參數,*args,比如內置函數max的參數就是一個可變參數,max(1,2,3,4,5)=5


(3)第三部分是一個關鍵字參數,比如內置函數int的第二個參數就是命名關鍵字參數,默認base=10,表示int轉換時默認是10進制的:



partial函數的作用就是:將所作用的函數作為partial()函數的第一個參數,原函數的各個參數依次作為partial()函數的后續參數,原函數有關鍵字參數的一定要帶上關鍵字,沒有的話,按原有參數順序進行補充。

文字描述顯得有些無力,我們下面就開始講一下,偏函數是怎么用的

 

二、偏函數的使用

A、偏函數的第二個部分(可變參數),按原有函數的參數順序進行補充,參數將作用在原函數上,最后偏函數返回一個新函數(類似於,裝飾器decorator,對於函數進行二次包裝,產生特殊效果;但又不同於裝飾器,偏函數產生了一個新函數,而裝飾器,可改變被裝飾函數的函數入口地址也可以不影響原函數)

案例:我們定義一個sum函數,參數為*args可變,計算這些可變參數的和。

擴展:我們想要對sum函數求和后的結果,再加上10加上20甚至加更多,得到一個新的結果

實現:我們分別用decorator和partial來實現,對比一下二者的區別

 

(一)裝飾器 decorator 實現

我們上一篇,剛剛學過decorator,所以這里,我們直接看demo,應該會覺得很容易上手和理解:

test.py

# /usr/bin/env Python3
# -*- encoding:UTF-8 -*-

from functools import wraps

def sum_add(*args1): #我們要給我們的裝飾器decorator,帶上參數
    def decorator(func):
        @wraps(func) #加上這句,原函數func被decorator作用后,函數性質不變
        def my_sum(*args2): #注意,參數要和原函數保持一致,真正實行擴展功能的是外層的裝飾器
            my_s = 0
            for n in args1:
                my_s = my_s +n #這個是我們新加的求和結果
            return func(*args2) + my_s #這個,我們在原求和函數的結果上再加上s,並返回這個值
        return my_sum #返回my_sum函數,該函數擴展原函數的功能
    return decorator  #返回我們的裝飾器

@sum_add(10,20) #啟用裝飾器 對sum函數進行功能擴展 
def sum(*args):
    s = 0
    for n in args:
        s = s+n
    return s
print(sum(1,2,3,4,5))
print(sum.__name__)

sum(1,2,3,4,5)返回的結果絕不是15,這樣就失去了裝飾器存在的意義,當然,這里,我們知道,sum最后返回的值應該是10+20+15 = 45,這樣一來,我們的decorator就實現了我們想要的擴展功能,最后,發現,原函數sum的name屬性,仍然是sum,說明,這種裝飾擴展功能,不影響我們的原函數:


 

(二)偏函數 partial function 實現

這才是我們本篇的重點,准備好了,我們就開始:

我們先來看下普通函數,我們是怎么來實現

A:普通函數可變參數順序執行

    # /usr/bin/env Python3  
    # -*- encoding:UTF-8 -*-  
      
    def sum(*args):  
        s = 0  
        for n in args:  
            s = s + n  
        return s  
    print(sum(10,20)+sum(1,2,3,4,5))  

我們如果想實現+10+20的效果,必須寫兩遍sum,這樣寫,顯然是最易懂的,但是,卻顯得很邋遢不專業,我們看下結果:


 

B:普通函數可變參數加關鍵字參數組合

針對上面的A過程,我們改下代碼,使我們的代碼看起來稍顯復雜,但是略顯專業:

    # /usr/bin/env Python3  
    # -*- encoding:UTF-8 -*-  
      
    def sum(*args,**others):  
        s = 0  
        for n in args:  
            s = s + n  
        s1 = 0   
        for k in others:  
            s1 = s1 + others[k] #我們還要算一下,關鍵字參數里蘊藏的求和結果,k是dict中的關鍵字key  
        return s+s1 #最終,我們實現擴展功能,順序參數和關鍵字參數結果相加  
          
    D= {'value1':10,'value2':20}   
    print(sum(1,2,3,4,5,**D))  

代碼看起來,是顯得專業了,但是感覺冗余,沒必要,復雜不是我們Python的風格,我們看下B的結果:


C:偏函數可變參數順序填充一步到位

上面A和B我們都說過了,這兩種方式都不好,顯然,這么簡單的事情,我們不必麻煩decorator了,那我們還有辦法沒?有,Python,給我們提供了偏函數,來吧,主角登場:

提示:兩種使用partial功能方式

(1)import functools                        -->functools.partial(func,*args)
(2)from   functools import partial -->partial(func,*args)

 

我們這里選第二種,我們看下demo:

    # /usr/bin/env Python3  
    # -*- encoding:UTF-8 -*-  
    from  functools import partial  
      
    def sum(*args):  
        s = 0  
        for n in args:  
            s = s + n  
        return s  
      
    sum_add_10    = partial(sum,10)    #10 作用在sum第一個參數的位置  
    sum_add_10_20 = partial(sum,10,20) #10 20 分別作用在sum第一個和第二個參數的位置  
    print('A____________我們看下原函數sum的函數地址入口:')  
    print(sum)  
    print('B______我們看下partial函數返回函數的地址入口:')  
    print(partial(sum,10))  
    print(sum_add_10(1,2,3,4,5))    # --> 10 + 1 + 2 + 3 + 4 + 5 = 25  
    print(sum_add_10_20(1,2,3,4,5)) # --> 10 + 20 + 1 + 2 + 3 + 4 + 5 = 45  

上面,可以看出,我們針對sum函數的求和結果,再加上10,或者加10加20,甚至加更多,都是可以通過偏函數來實現的,注意偏函數的第二部分,參數是可變的,是按順序走的,因此,偏函數產生的新函數,sum_add_10 實際上等同於sum(10,*args):


 

通過幾個例子,我們最終發現,還是偏函數比較方便,一行代碼就搞定了,而且新定義的函數,可以根據函數名很容易知道,這個函數擴展的原函數是哪個,實現的效果是什么:

 


 

 

B、偏函數的第三個部分(關鍵字參數),按原有函數的關鍵字參數進行填補,參數將作用在原函數上,最后偏函數返回一個新函數

 

案例:我們定義一個mod求余函數,兩個參數,一個是被除數,一個是除數,除數我們這里用命名關鍵字參數表示,默認值2

擴展:我們的除數不固定,可以是對2就行求余,也可以對3,對4,總之我們需要指定除數的值

返回結果: True 或 False

實現:原函數實現和partial函數實現

demo如下:

    # /usr/bin/env Python3  
    # -*- encoding:UTF-8 -*-  
    import  functools   
    def mod(m,*,key=2):  
     return m % key == 0  
    mod_to_2 = functools.partial(mod,key=2)  
    print('A__3___使用原函數的默認關鍵字參數對2進行求余:')  
    print(mod(3))                           #對2進行求余-- 原函數 使用默認參數  
    print('B__3___使用偏函數對2進行求余:')  
    print(mod_to_2(3))                      #對2進行求余-- 新函數 --偏函數產生  
    mod_to_5 = functools.partial(mod,key=5)   
    print('C__25___使用原函數的關鍵字參數對5進行求余:')  
    print(mod(25,key=5))                    #對5進行求余 -- 原函數  
    print('D__25___使用偏函數對5進行求余:')  
    print(mod_to_5(25))                     #對5進行求余 -- 新函數--偏函數產生  

我們看下結果:



我們發現,實際上,偏函數的作用,其實和原函數差不多,只不過,我們要多次調用原函數的時候,有些參數,我們需要多次手動的去提供值,比如上述的對5進行求余,如果我們想知道,15,45,30這些數是否能夠被5整除,那么,我們用原函數的話,就需要寫三次,key=5,然而,我們用偏函數的話,只需要重復調用新產生的函數mod_to_5(15 or 45 or 30)即可,至於除數5,偏函數已經為我們設定了,因此:

當函數的參數個數太多,需要簡化時,使用 functools.partial 可以創建一個新的函數,這個新函數可以固定住原函數的部分參數,從而在調用時更簡單。當然,decorator也可以實現,如果,我們不嫌麻煩的話。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM