Python乾貨-高階函式
認識高階函式
函式與變數
print(abs(-1))
1
上例中,abs() 是 python 內建函式,用來求一個數值的絕對值
就以這個函式為例,abs() 是個函式,那 abs 是這個函式的名字,() 才定義其是一個方法,如果只輸入 abs,會發生什麼情況?
abs
<function abs>
從輸出上來看,abs 是一個方法
也就是說,abs 是一個方法的名,它指向了該方法的記憶體地址
既然這個方法有地址,同樣可以將它的地址賦給一個變數
_func = abs
print(_func(-1))
1
也就是說,_func 也指出了 abs 所指向的方法地址,在其後面加上括號後,便可呼叫所指向的方法
所以,函式名也是變數
函式可以被當作引數傳入函式
那麼一個函式就可以接收另一個函式作為引數,這種函式就稱之為高階函式
定義一個簡單的高階函式:
# 定義一個高階函式,接收兩個方法作為引數
def get_name(f_name, l_name):
# 列印全名
print(f_name, l_name)
# 定義f_name()函式,返回frist name
def first_name(name):
return name
# 定義l_name()函式,返回last name
def last_name(name):
return name
# 將first_name()與last_name當作引數傳入get_name()函式中
get_name(first_name('Jack'), last_name('Chen'))
Jack Chen
上例中,get_name()就是一個高階函式
認識其它高階函式
map()函式
map()是python的內建函式,它的語法如下:
map(func, *iterables) --> map object
第一個引數是一個方法,第二個引數是一個列表(序列);map的作用是將序列iterables中的元素依次作用到函式func上,並返回一個新的序列物件
示例:
# 定義一個函式,返回一個數的平方
def func_square(num):
return num** 2
# 呼叫map()函式
num_list = map(func_square, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(num_list))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
從結果上來看,序列引數中的每個元素都被作用到func_square函式中,得到一個平方值,最終返回所有數的平方
示例:
print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
該例中的序列引數被str()方法都轉換成了字串格式,這也是另一種運用
reduce()函式
reduce()函式將一個函式作用在一個序列上,這個函式必須接收兩個引數,reduce()函式會繼續和序列的下一個元素做累積運算
示例:
# 從functolls模組中匯入reduce函式
from functools import reduce
# 定義一個加法函式
def add(x, y):
return x+y
# 呼叫reduce()函式
result = reduce(add, [1, 2, 3, 4, 5])
print(result)
15
序列[1, 2, 3, 4, 5]
的每一個元素作用到add()方法上,將該函式得到的結果再與下一個元素傳入add()方法中,直到結束,得到的結果就是1+2+3+4+5=15
示例:如何將序列[1, 2, 3, 4, 5]
轉換成數字12345
# 從functolls模組中匯入reduce函式
from functools import reduce
# 定義一個轉換函式
def fn(x, y):
return x * 10 + y
# 呼叫reduce()函式
result = reduce(fn, [1, 2, 3, 4, 5])
print(result)
12345
這裡定義了一個函式fn(),該函式可以將傳入的丙個引數做十位數與個位數的運算,再將得出的結果與下一個元素做同樣的處理,原來的十位數再乘10變成了百位數,個位數則成了十位數,新的y元素則成了個位數,依此類推,最後得出了結果12345
map() 函式與 reduce() 函式的結合
示例:將字串轉換成列表,再將該列表做累加運算
# 從functolls模組中匯入reduce函式
from functools import reduce
# 定義一個轉換函式
def fn(x, y):
return x * 10 + y
# 定義一個數字轉換字元的方法
def char_num(x):
return int(x)
# 呼叫reduce()函式
result = reduce(fn, map(char_num, '12345'))
print(result)
12345
map() 函式會將字串 '12345’當作一個字元序列今次作用到 char_num() 方法上,最終會返回一個數字列表,這個列表又會依次作用到 fn() 函式上,得到的結果與上一個例子結果是一致的
python提供了更加強大的函式程式設計,可以將多個方法封裝到一個方法裡,上面的例子中的兩個方法就可以包裝成一個方法,使用的時候也只需要呼叫一次即可同,如下:
# 從functolls模組中匯入reduce函式
from functools import reduce
# 頂層包裝函式
def func_all():
# 定義一個轉換函式
def fn(x, y):
return x * 10 + y
# 定義一個數字轉換字元的方法
def char_num(x):
return int(x)
# 呼叫reduce()函式
return reduce(fn, map(char_num, '12345'))
print(func_all())
12345
也可以使用lambda函式進行簡化,如下:
# 從functolls模組中匯入reduce函式
from functools import reduce
# 定義一個數字轉換字元的方法
def char_num(x):
return int(x)
# 呼叫reduce()函式
result = reduce(lambda x, y : x * 10 +y, map(char_num, '12345'))
print(result)
12345
filter() 函式
filter() 函式接收一個函式和一個序列作為引數,並將序列中的每一個元素都作用到傳入函式上,根據該函式返回的布林值,來決定當前元素是否保留
示例:篩選一個序列,保留奇數
# 奇數篩選函式
def is_odd(x):
return x % 2 == 1
# 呼叫filter()函式
result = filter(is_odd, [1, 2, 3, 4, 5])
print(list(result))
[1, 3, 5]
示例:刪除掉序列中的空字串
# 篩選空字串
def empty_str(x):
return x.strip() != ''
# 呼叫filter()函式
result = filter(empty_str, ['', '2', 'a', ' '])
print(list(result))
['2', 'a']
注意:filter()函式返回的是一個Iterator,也就是一個惰性序列,所以要強迫filter()完成計算結果,需要用list()函式獲得所有結果並返回list
示例:篩選質數
質數定義為在大於1的自然數中,除了1和它本身以外不再有其他因數。
因數,或稱為約數,數學名詞。定義:整數a除以整數b(b≠0) 的商正好是整數而沒有餘數,我們就說b是a的因數。0不是0的因數。
計算質數的一個方法是埃氏篩法,它的演算法理解起來非常簡單:
首先,列出從2開始的所有自然數,構造一個序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取序列的第一個數2,它一定是素數,然後用2把序列的2的倍數篩掉:
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一個數3,它一定是素數,然後用3把序列的3的倍數篩掉:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一個數5,然後用5把序列的5的倍數篩掉:
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
不斷篩下去,就可以得到所有的素數。
# 先構造一個從3開始的奇數序列
def _odd_iter():
n = 1
while True:
n = n + 2
yield n # yield返回的是一個生成器,所包含的序列將是無限的
# 定義一個篩選函式,
def _not_divisible(n):
return lambda x: x % n > 0 # 這裡將lambda匿名函式返回
# 定義一個生成器函式
def primes():
yield 2 # 事先在生成器裡返回了2
it = _odd_iter() # 初始序列,這裡都是奇數,返回的是一個生成器
while True:
n = next(it) # 返回序列的第一個數,之後返回下一個
yield n
it = filter(_not_divisible(n), it) # 構造新序列,每次將所有奇數作用於篩選函式返回的函式上
# 列印100以內的素數:
l = []
for n in primes():
if n < 100:
l.append(n)
else:
break
print(l)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
yield 是一個類似 return 的關鍵字,只是這個函式返回的是個生成器
當你呼叫這個函式的時候,函式內部的程式碼並不立馬執行 ,這個函式只是返回一個生成器物件
當你使用for進行迭代的時候,函式中的程式碼才會執行
sorted() 函式
sorted() 函式是 python 中內建的函式,可以對 list 進行排序
示例:
l = sorted([1, 4, 5, 2, 3])
print(l)
[1, 2, 3, 4, 5]
此外,sorted()函式也是一個高階函式,它還可以接收一個key函式來實現自定義的排序
示例:按絕對值大小排序
# 按大小排序
l = sorted([1, -5, 3, -2, -4])
print(l)
# 按絕對值排序
l = sorted([1, -5, 3, -2, -4], key=abs)
print(l)
[-5, -4, -2, 1, 3]
[1, -2, 3, -4, -5]
key指定的函式將作用於list的每一個元素上,並根據key函式返回的結果進行排序
sorted() 也可以對字串進行排序,示例:
l_str = sorted(['hello', 'my', 'name', 'is', 'Jack'])
print(l_str)
['Jack', 'hello', 'is', 'my', 'name']
預設情況下,對字串排序,是按照ASCII的大小比較的,由於’J’ < ‘a’,結果,大寫字母J會排在小寫字母h的前面
如果純粹的想按字母進行排序,而不分大小寫,則可以將所有的字母都轉換成小寫,再來進行排序,示例如下:
l_str = sorted(['hello', 'my', 'name', 'is', 'Jack'], key=str.lower)
print(l_str)
['hello', 'is', 'Jack', 'my', 'name']
還可以反向排序,需要第三個引數reverse=True
,示例如下:
l_str = sorted(['hello', 'my', 'name', 'is', 'Jack'], key=str.lower, reverse=True)
print(l_str)
['name', 'my', 'Jack', 'is', 'hello']