Python學習筆記 -- 函數語言程式設計之高階函式
函數語言程式設計
函數語言程式設計(Functional Programming),是一種抽象程度很高的程式設計正規化,純粹的函數語言程式設計語言編寫的函式沒有變數。因此,任意一個函式,只要輸入是確定的,輸出就是確定的。
函數語言程式設計是一種ofollow,noindex">"程式設計正規化" (programming paradigm)。它屬於"結構化程式設計" 的一種,主要思想是把運算過程儘量寫成一系列巢狀的函式呼叫。
函數語言程式設計的一個特點就是,允許把函式本身作為引數傳入另一個函式,還允許返回一個函式!Python對函數語言程式設計提供部分支援。由於Python允許使用變數,因此,Python不是純函數語言程式設計語言。
更多內容
高階函式
函數語言程式設計中,可以將函式當作變數一樣使用。接受函式為引數,或者把函式作為結果返回的函式稱為高階函式(Higher-order Functions) 。
def double(x): return 2 * x def square(x): return x * x def func(g, arr): return [g(x) for x in arr] >>> arr1 = func(double, [1, 2, 3, 4]) >>> arr2 = func(square, [1, 2, 3, 4]) >>> arr1 [2, 4, 6, 8] >>> arr2 [1, 4, 9, 16]
map()/reduce() /filter() /sorted() /apply() 是 Python 中較為常用的高階函式,它們為函數語言程式設計提供了不少便利。
Python 3 中,map
和filter
還是內建函式,但是由於引入了列表推導和生成器表示式,它們變得沒那麼重要了。列表推導或生成器表示式具有map
和filter
兩個函式的功能,而且更易於閱讀。apply
函式在Python 2.3 中標記為過時,在Python 3 中移除了,因為不再需要它了。如果想使用不定量的引數呼叫函式,可以編寫fn(*args, **keywords)
,不用再編寫apply(fn, args,kwargs)
。
map()
map
函式將傳入的函式依次作用到序列的每個元素,並把結果作為新的Iterator
返回。map
函式語法:
# map(function, iterable, ...) ## 引數 —— function : 函式 ;iterable : 一個或多個序列 ## 返回值 —— Python 2.x 返回列表; Python 3.x 返回迭代器(map) def square(x):# 計算平方數 return x ** 2 >>> map(square, [1,2,3,4,5])# 計算列表各個元素的平方 <map object at 0x000001B9F81ADEB8> >>> map(lambda x: x ** 2, [1, 2, 3, 4, 5])# 使用 lambda 匿名函式 # [1, 4, 9, 16, 25]# python2輸出結果 <map object at 0x000001B9F81ADEB8># python3輸出結果(可使用list()函式對map函式返回結果進行轉換) >>> list(map(lambda x: x ** 2, [1, 2, 3, 4, 5])) [1, 4, 9, 16, 25] # map實現list的值的格式批量轉換 >>> list(map(str, [1, 2, 3, 4])) ['1', '2', '3', '4'] >>> list(map(int, ['1', '2', '3', '4'])) [1, 2, 3, 4] # 提供了兩個列表,對相同位置的列表資料進行相加 >>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]) [3, 7, 11, 15, 19] # -------- 列表推導式實現上述功能 ----------- >>> [square(n) for n in [1,2,3,4,5]] [1, 4, 9, 16, 25] >>> [str(n) for n in [1,2,3,4]] ['1', '2', '3', '4'] >>> [int(n) for n in ['1', '2', '3', '4']] [1, 2, 3, 4]
reduce()
reduce()
函式會對引數序列中元素進行累積。函式將一個數據集合(連結串列,元組等)中的所有資料進行下列操作:用傳給reduce()
中的函式function
(有兩個引數)先對集合中的第 1、2 個元素進行操作,得到的結果後繼續和序列的下一個元素做累積計算,直到累積到列表最後一個數據。Python 2 中,reduce 是內建函式,但是在Python 3 中放到functools 模組裡了。reduce()
函式語法:
# reduce(function, iterable[, initializer]) ## 引數 —— function:函式,有兩個引數; iterable : 可迭代物件; initializer:可選,初始引數 ## 返回值 —— 返回函式計算結果。 # 序列求和實現 from functools import reduce# Python3 需要引入 def add(x, y) :# 兩數相加(或使用 from operator import add) return x + y >>> reduce(add, [1,2,3,4,5])# 計算列表和:1+2+3+4+5 15 >>> reduce(lambda x, y: x * y, [1, 2, 3, 4])# 相當於 ((1 * 2) * 3) * 4 24 >>> reduce(lambda x, y: x * y, [1, 2, 3, 4], 5) # ((((5 * 1) * 2) * 3)) * 4 120 >>> reduce(lambda x, y: x / y, [2, 3, 4], 72)#(((72 / 2) / 3)) / 4 3 >>> reduce(lambda x, y: x + y, [1, 2, 3, 4], 5)# ((((5 + 1) + 2) + 3)) + 4 15 >>> reduce(lambda x, y: x - y, [8, 5, 1], 20)# ((20 - 8) - 5) - 1 6 >>> f = lambda a, b: a if (a > b) else b# 兩兩比較,取最大值 >>> reduce(f, [5, 8, 1, 10]) 10
** 注意:** 在 Python3 中,reduce() 函式已經被從全域性名字空間裡移除了,它現在被放置在 fucntools 模組裡,如果想要使用它,則需要通過引入 functools 模組來呼叫 reduce() 函式:
from functools import reduce
filter()
filter()函式用於過濾序列,過濾掉不符合條件的元素,Python2返回過濾後的列表,Python3返回迭代器物件(filter),如果要轉換為列表,可以使用 list() 來轉換。
# filter(function, iterable) ## 引數 —— function : 判斷函式; iterable : 可迭代物件 ## 返回值 —— 返回一個迭代器物件 # 過濾出列表中的所有奇數 def is_odd(n) : return n % 2 == 1 >>> list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) [1, 3, 5, 7, 9] # 過濾出1~100中平方根是整數的數 import math def is_sqr(n) : return math.sqrt(n) % 1 == 0 >>> list(filter(is_sqr, range(1, 101))) [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
注意:Python3 中返回到是一個filter
類,filter
類實現了__iter__
和__next__
方法,可以看成是一個迭代器, 有惰性運算的特性,相對 Python2提升了效能,可以節約記憶體。
sorted()
sorted()
函式對所有可迭代的物件進行排序操作。
sort 與 sorted 區別:
- sort 是應用在 list 上的方法,sorted 可以對所有可迭代的物件進行排序操作。
- list 的 sort 方法返回的是對已經存在的列表進行操作,而內建函式 sorted 方法返回的是一個新的 list,而不是在原來的基礎上進行的操作。
sorted()
語法:
# sorted(iterable, key=None, reverse=False) ## 引數 —— iterable -- 可迭代物件; key -- 主要是用來進行比較的元素,只有一個引數,具體的函式的引數就是取自於可迭代物件中,指定可迭代物件中的一個元素來進行排序; reverse -- 排序規則,reverse = True 降序 , reverse = False 升序(預設) ## 返回值 —— 重新排序的列表 >>>sorted([5, 2, 3, 1, 4]) [1, 2, 3, 4, 5] # 使用集合的sort()方法,實現類似結果 >>>a = [5,2,3,1,4] >>> a.sort() >>> a [1,2,3,4,5] # sort()及sorted()的區別在於, sort()返回None,排序操作直接作用在原list上,而sorted()排序後會返回新的list >>> a = [1,3,2,4];id(a); a.sort(); id(a) 1734959842440 1734959842440 >>> b = [4,3,1,2]; id(b);id(sorted(b)); 1734959953864 1734959953672 # sort()和sorted() 另一個區別在於list.sort() 方法只為 list 定義。而 sorted() 函式可以接收任何的 iterable。 >>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'}) [1, 2, 3, 4, 5] # 利用key進行倒序排序 >>> example_list = [5, 0, 6, 1, 2, 7, 3, 4] >>> result_list = sorted(example_list, key=lambda x: x * -1) >>> print(result_list) [7, 6, 5, 4, 3, 2, 1, 0] # 進行反向排序,也可傳入第三個引數 reverse=True: >>>example_list = [5, 0, 6, 1, 2, 7, 3, 4] >>> sorted(example_list, reverse=True) [7, 6, 5, 4, 3, 2, 1, 0]
返回函式
高階函式除了可以接受函式作為引數外,還可以把函式作為結果值返回。
# 返回累加函式變數 def lazy_sum(*args):# 外部函式 def sum():#內部函式 可以引用外部函式lazy_sum的引數和區域性變數 ax = 0 for n in args: ax = ax + n return ax return sum# 返回函式變數(返回函式sum時,lazy_sum相關引數和變數都儲存在返回的函式中,閉包) >>> f = lazy_sum(1, 3, 5, 7, 9) >>> f <function lazy_sum.<locals>.sum at 0x00000193F3C89048> >>> f()# 返回的函式並沒有立刻執行,而是直到呼叫了f()才執行 25 # 呼叫lazy_sum()時,每次呼叫都會返回一個新的函式 >>> f1 = lazy_sum(1, 3, 5, 7, 9) >>> f2 = lazy_sum(1, 3, 5, 7, 9) >>> f1==f2# f1()和f2()的呼叫結果互不影響 False
閉包
一個函式返回了一個內部函式,該內部函式引用了外部函式的相關引數和變數,我們把該返回的內部函式稱為閉包(Closure)
- 閉包的最大特點就是引用了自由變數,即使生成閉包的環境已經釋放,閉包仍然存在。
- 閉包在執行時可以有多個例項,即使傳入的引數相同。
# 呼叫lazy_sum()時,每次呼叫都會返回一個新的函式 >>> f1 = lazy_sum(1, 3, 5, 7, 9) >>> f2 = lazy_sum(1, 3, 5, 7, 9) >>> f1 == f2# f1()和f2()的呼叫結果互不影響 False
- 利用閉包,還可以模擬類的例項。
# 構造一個類,用於求一個點到另一個點的距離: from math import sqrt >>> class Point(object): def __init__(self, x, y): self.x, self.y = x, y def get_distance(self, u, v): distance = sqrt((self.x - u) ** 2 + (self.y - v) ** 2) return distance >>> pt = Point(7, 2)# 建立一個點 >>> pt.get_distance(10, 6)# 求到另一個點的距離 5.0 # 用閉包來實現: def point(x, y): def get_distance(u, v): return sqrt((x - u) ** 2 + (y - v) ** 2) return get_distance >>> pt = point(7, 2) >>> pt(10, 6) 5.0
注意:儘量避免在閉包中引用迴圈變數,或者後續會發生變化的變數。
def count(): funcs = [] for i in [1, 2, 3]: def f(): return i funcs.append(f) return funcs >>> f1, f2, f3 = count() >>> f1() 3 >>> f2() 3 >>> f3() 3 # 原因在於上面的函式 f 引用了變數 i,但函式 f 並非立刻執行,當 for 迴圈結束時,此時變數 i 的值是3,funcs 裡面的函式引用的變數都是 3,最終結果也就全為 3。 # 可以再建立一個函式,並將迴圈變數的值傳給該函式 def count(): funcs = [] for i in [1, 2, 3]: def g(param): f = lambda : param# 這裡建立了一個匿名函式 return f funcs.append(g(i))# 將迴圈變數的值傳給 g return funcs >>> f1, f2, f3 = count() >>> f1() 1 >>> f2() 2 >>> f3() 3