Python函數語言程式設計-高階函式、匿名函式、裝飾器、偏函式

糖寶發表於2019-02-16

本篇文章我們來介紹下Python函數語言程式設計的知識。最主要的一點,Python中的函式是物件,可以複製給變數!好了,我們來介紹幾個Python函數語言程式設計中的要點,包括高階函式、匿名函式、裝飾器、偏函式等等。精彩內容,不容錯過!

Python函數語言程式設計-高階函式、匿名函式、裝飾器、偏函式
顯示在電腦螢幕上的計算機程式原始碼,可用作網路科技等素材或背景。
如果你在學習Python的過程中遇見了很多疑問和難題,可以加-q-u-n 227 -435-450裡面有軟體視訊資料免費
1、高階函式

函式本身也可以賦值給變數,即:變數可以指向函式。如果一個變數指向了一個函式,那麼,可以通過該變數來呼叫這個函式。

f = abs
f(-10) # 10
既然變數可以指向函式,函式的引數能接收變數,那麼一個函式就可以接收另一個函式作為引數,這種函式就稱之為高階函式。

def add_abs(x,y,f):
return f(x) + f(y)
f = abs
add_abs(-5,6,f) # 11
接下來,我們介紹幾個重點的高階函式。

map函式

map()函式接收兩個引數,一個是函式,一個是Iterable,map將傳入的函式依次作用到序列的每個元素,並把結果作為新的Iterator返回。我們之前介紹過了,Iterator是惰性序列,需要通過list()函式讓它把返回結果變為list。

def f(x):
return x * x
r = list(map(f,[1,2,3,4,5,6,7,8,9,10]))
r
輸出結果為:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
reduce函式

reduce把一個函式作用在一個序列[x1, x2, x3, …]上,這個函式必須接收兩個引數,reduce把結果繼續和序列的下一個元素做累積計算:

def f(x,y):
return x + y
reduce(f,[1,2,3,4,5]) # 15
filter函式

和map()類似,filter()也接收一個函式和一個序列。和map()不同的是,filter()把傳入的函式依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。filter()函式返回的是一個Iterator,也就是一個惰性序列,同樣需要通過list()函式讓它把返回結果變為list。

def is_odd(n):
return n%2==1
list(filter(is_odd,[1,2,3,4,5,6,7,8,9]))
結果為:

[1, 3, 5, 7, 9]
sorted函式

sorted()函式也是一個高階函式,它還可以接收一個key函式來實現自定義的排序,例如按絕對值大小排序或者按照小寫字母順序進行排序:

print(sorted([36,5,-12,9,-21],key=abs))
print(sorted([`bob`, `about`, `Zoo`, `Credit`],key=str.lower))
結果為:

[5, 9, -12, -21, 36]
[`about`, `bob`, `Credit`, `Zoo`]
2、函式作為返回值

高階函式除了可以接受函式作為引數外,還可以把函式作為結果值返回。

我們來實現一個可變引數的求和,如果不需要立刻求和,而是在後面的程式碼中,根據需要再計算怎麼辦?可以不返回求和的結果,而是返回求和的函式:

def lazy_sum(*args):
def sum():
res = 0
for n in args:
res += n
return res
return sum
f = lazy_sum(1,3,5,7,9)
f()
在這個例子中,我們在函式lazy_sum中又定義了函式sum,

並且,內部函式sum可以引用外部函式lazy_sum的引數和區域性變數,

當lazy_sum返回函式sum時,相關引數和變數都儲存在返回的函式中,

這種稱為“閉包(Closure)”的程式結構擁有極大的威力。

請再注意一點,當我們呼叫lazy_sum()時,每次呼叫都會返回一個新的函式,即使傳入相同的引數:

f1 = lazy_sum(1,3,5,7,9)
f2 = lazy_sum(1,3,5,7,9)
f1 == f2 # False
另一個需要注意的問題是,返回的函式並沒有立刻執行,而是直到呼叫了f()才執行。我們來看一個例子:

def count():
fs = []
for i in range(1,4):
def f():
return i * i
fs.append(f)
return fs
f1,f2,f3 = count()
print(f1())
print(f2())
print(f3())
輸出結果為:

9
9
9
全部都是9!原因就在於返回的函式引用了變數i,但它並非立刻執行。等到3個函式都返回時,它們所引用的變數i已經變成了3,因此最終結果為9。如果一定要引用迴圈變數怎麼辦?方法是再建立一個函式,用該函式的引數繫結迴圈變數當前的值,無論該迴圈變數後續如何更改,已繫結到函式引數的值不變:

def count():
fs = []
def sub(j):
def f():
return j * j
return f
for i in range(1,4):
fs.append(sub(i))
return fs
f1,f2,f3 = count()
print(f1())
print(f2())
print(f3()
結果為:

1
4
9
3、匿名函式lambda

當我們在傳入函式時,有些時候,不需要顯式地定義函式,直接傳入匿名函式更方便。相信大家對於匿名函式一定不陌生,其實就是我們常說的lambda函式:

list(map(lambda x:x * x,[1,2,3,4,5,6,7,8,9]))
def build(x,y):
return lambda:x x + y y
reduce(lambda x,y:x + y,[1,2,3,4,5,6,7,8,9])
4、裝飾器

在程式碼執行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。

本質上,decorator就是一個返回函式的高階函式。

所以,假設我們要定義一個能夠列印當前函式名的decorator:

def log(func):
def wrapper(args,*kwargs):
print(`call %s()` % func.__name__)
return func(args,*kwargs)
return wrapper
@log
def helloworld():
print(`hello world`)
helloworld()
執行結果為:

call helloworld()
hello world
下面的例子中,正是由於wrapper中把 func(args,*kwargs)進行return,因此函式得以執行。

如果decorator本身需要傳入引數,那就需要編寫一個返回decorator的高階函式,寫出來會更復雜。比如,要自定義log的文字:

def log(text):
def decorator(func):
def wrapper(args,*kwargs):
print(`%s %s():` % (text,func.__name__))
return func(args,*kwargs)
return wrapper
return decorator
@log(`execute`)
def helloworld():
print(`hello world`)
helloworld()
上面程式碼的執行過程相當於helloworld = log(`execute`)(helloworld)

我們講了函式也是物件,它有name等屬性,但你去看經過decorator裝飾之後的函式,它們的name已經從原來的`helloworld`變成了`wrapper`:

helloworld.__name__ #`wrapper`
如果需要把原始函式的name等屬性複製到wrapper()函式中,使用Python內建的functools.wraps函式,程式碼如下:

import functools
def log(func):
@functools.wraps(func)
def wrapper(args, *kw):
print(`call %s():` % func.__name__)
return func(args, *kw)
return wrapper
@log
def helloworld():
print(`hello world`)
helloworld.__name__
此時的輸出就變為了`helloworld`

5、偏函式

一般的函式有許多需要定義的引數,假設我們想要固定其中的某些引數,返回一些新的函式,我們就可以使用functools.partial幫助我們建立一個偏函式,從而使得呼叫變得簡單

import functools
int2 = functools.partial(int, base=2)
int2(`10010`) # 18
當然我們也可以穿入一個函式字典:

kw = { `base`: 2 }
int(`10010`, **kw)
當傳入的引數沒有對應的key時,它預設時作為*args的一部分自動加到左邊,因此下面的函式相當於比較max(10,5,6,7),返回10:

max2 = functools.partial(max, 10)
max2(5, 6, 7)

相關文章