1. 程式人生 > 其它 >python柯里化與反柯里化

python柯里化與反柯里化

例子 f(x,y,z)=f(x)(y,z)

def add(a, b, c):
    return a + b + c
 
def currying_add(func):
    def wrapper(a, c, b=666):
        return func(a, b, c)
    return wrapper
 
 
result = currying_add(add)(1,2)
print(result)  # 669

運用在裝飾器

def currying_add(func):
    def wrapper(a, c, b=666):
        return
func(a, b, c) return wrapper # 使用裝飾器符號來定義函式add,add = currying_add(add) @currying_add def add(a, b, c): return a + b + c result = add(1,2) print(result) # 669

------------------------------------------------------------

一、什麼是函式柯里化(Currying)

函式柯里化是解釋型語言常見的一種特性,常見的語言比如python、javascript都支援函式柯里化

有兩種理解,當然這兩種理解的本質實際上是表達同一層含義,如下:

定義一:

柯里化:一個函式中有個多個引數,想固定其中某個或者幾個引數的值,而只接受另外幾個還未固定的引數,這樣函式演變成新的函式。

定義二:

函式柯里化(currying)又稱部分求值。一個 currying 的函式首先會接受一些引數,接受了這些引數之後,該函式並不會立即求值,而是繼續返回另外一個函式,剛才傳入的引數在函式形成的閉包中被儲存起來。待到函式被真正需要求值的時候,之前傳入的所有引數都會被一次性用於求值。

定義三:

一些函式式語言的工作原理是將多引數函式語法轉化為單引數函式集合,這一過程稱為柯里化,它是以邏輯學家Haskell Curry的名字命名的。Haskell Curry從早期概念中發展出了該理論。其形式相當於將z = f(x, y)轉換成z = f(x)(y)的形式,原函式由兩個引數,現在變為兩個接受單引數的函式,在函數語言程式設計語言中,這種特性很常見。

柯里化是一種將多引數函式轉化為單引數高階函式的技術。例如函式 f(x,y) -> z,對於給定的兩個引數 x 和 y ,該函式會返回 z 值作為結果。可以將 f(x,y) 柯里化為兩個函式:f(x)(y) -> z。

f(x1,x2,x3,x4) ——> f(x1)(x2)(x3)(x4)

1.1 從一個例子來說起——按照定義二來實現

現在有一個函式add1,作用是實現三個數字相加,現在我們想要固定其中的第二個引數不變,呼叫的時候只指定第一個、第三個引數,艱難單的實現如下:

def add1(a, b, c):
    return a + b + c
 
def add2(a, b):
    return add1(a, 666, b)
 
result = add2(12,13)
print(result)

不錯,這就是函式柯里化的簡單實現,因為在python中一切皆物件,函式也是一種物件,所以實現起來很簡單靈活,

1.2 python函式柯里化的不同實現方法——基於定義一、定義二

(1)通過functools提供的偏函式來實現

from functools import partial
 
def add1(a, b, c):
    return a + b + c
 
add2 = partial(add1, b=666)
# 使用 partial 來實現偏函式時,要指定引數名
result = add2(a = 12, c = 13)
print(result)

(2)定義兩個函式,用函式2來包裝函式1

如上面所示,用add2來包裝add1,這裡不再贅述

(3)使用lambda表示式來完成

既然可以用一個函式包裝另一個函式,自然可以使用lambda表示式來實現,如下所示:

def add1(a, b, c):
    return a + b + c
 
# 使用lambda表示式柯里化,固定第二個引數b=666
add2 = lambda x,y:add_number(x,666,y)
result = add2(12, 13)
print(result)

(4)使用python的裝飾器來實現

這就是最開始為什麼強調深入理解python高階特性“裝飾器”是非常有幫助的,關於python裝飾器的系列文章,可以參考我前面的部落格。程式碼如下:

def add(a, b, c):
    return a + b + c
 
def currying_add(func):
    def wrapper(a, c, b=666):
        return func(a, b, c)
    return wrapper
 
 
result = currying_add(add)(1,2)
print(result)  # 669

如果是使用裝飾器符號@該怎麼實現呢?如下:

def currying_add(func):
    def wrapper(a, c, b=666):
        return func(a, b, c)
    return wrapper
 
# 使用裝飾器符號來定義函式add,add = currying_add(add)
@currying_add
def add(a, b, c):
    return a + b + c
 
result = add(1,2)
print(result)  # 669

(5)通過pymond模組;來實現currying

from pymonad import curry

1.2 python函式柯里化的不同實現方法——基於定義三

這裡是實現以下功能,一個函式有多個引數,將其拆分成幾個函式,每一個函式只具有其中的部分引數,實現函式引數的拆分。

如下程式碼:

def add(x, y):
    return x + y
 
# 柯里化
def currying_add(x):
    def inc(y):
        return x + y
    return inc  # 返回函式
 
result = currying_add(2)(3)
print(result)   # 5

柯里化的通俗總結:

不管是基於第一種、第二種定義,他更加側重於將一個函式的多個引數中的其中幾個先固定,這樣減少引數的數量

還是基於第三中定義,它更加側重於將函式的多個引數拆分成少量幾個引數的組成,其實它們的本質都是一樣,它們的實現核心思想也是一樣的,真正核心的地方就在於“高階函式”。

注意:柯里化的第一種定義、第二種定義看似是和函式定義預設值相似,但是預設值只能定義單個固定值,而柯里化能泛化出多個不同固定值但是相同計算規則的函式。

二、什麼是函式反柯里化?

前面說的函式柯里化是實現這樣的功能,即:

f(x1,x2,x3,x4) ——> f(x1)(x2)(x3)(x4)

那麼實際上,函式的反柯里化就是恰好反過來的過程,即實現

f(x1)(x2)(x3)(x4)——>f(x1,x2,x3,x4)

含義的確是比較好理解,但是Python中比較少的用到,因為我們更多的使用函式柯里化操作,我們很少見到python中呼叫函式通過下面這樣的方式呼叫吧,即:

f(x1)(x2)(x3)(x4)

所以函式反柯里化實際上一般都是和函式柯里化來結合使用,我們參見下面的例子:

# 定義加法函式
def add(x, y):
    return x + y
 
# 柯里化
def currying_add(x):
    def inc(y):
        return x + y
    return inc  # 返回函式
 
# 因為currying_add()是一個柯里化函式,現在我針對他定義一個反柯里化函式,如下:
def anti_currying_add(x,y):
    return currying_add(x)(y)
 
result = anti_currying_add(2,3)  # 呼叫反柯里化函式
 
print(result)   # 5

就是這麼簡單,其實這一個特性最重要的理解就在於“高階函式”。

原文:https://blog.csdn.net/qq_27825451/article/details/102666716