1. 程式人生 > >Python乾貨-高階函式

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']