這篇文章是閱讀Asabeneh的30 Days Of Python: Day 14 - Higher Order Functions後的學習筆記與心得。
在Python中函式(functions)被當作頭等公民(first class citizens),允許使用以下的操作:
高階函式(higher order function)就是依據不同的參數回傳不同的函式:
比如下面這個例子,higher_order_function
接受兩個參數,第一個是任何可以接受List型別做為參數的函式,另一個參數則是一個List:
def higher_order_function(cb, lst):
return cb(lst)
ages = [20, 20, 4, 24, 25, 22, 26, 20, 23, 22, 26]
print(higher_order_function(sorted, ages))
# [4, 20, 20, 20, 22, 22, 23, 24, 25, 26, 26]
print(higher_order_function(set, ages))
# {4, 20, 22, 23, 24, 25, 26}
另一個例子,higher_order_function
依據傳入值不同指派不同的函式給變數result
:
def square(x):
return x ** 2
def cube(x):
return x ** 3
def absolute(x):
return x if x >= 0 else -(x)
def higher_order_function(type):
if type == "square":
return square
elif type == "cube":
return cube
elif type == "absolute":
return absolute
result = higher_order_function("square")
print(result(3)) # 9
result = higher_order_function("cube")
print(result(3)) # 27
result = higher_order_function("absolute")
print(result(-3)) # 3
概念跟JavaScript中一樣;函式中有定義另一個函式,並且被放入回傳值中,透過回傳的add
函式,可以觸及add_ten
函式內部的變數ten
:
def add_ten():
ten = 10
def add(num)
return num + ten
return add
closure_result = add_ten()
print(closure_result(5)) # 15
print(closure_result(10)) # 20
就原文所說,這是Python中一種設計模式(design pattern),讓使用者能將新的功能加到現有的物件中,但不用更改現有物件的結構。在要做為裝飾器的函式前加上@
使其成為裝飾器。
如文章前段所提,我們可以用高階函式uppercase_decorator
來創造出一個新的函式,這個函式基於greeting
的回傳值做了改變並回傳出來。這個做法下,要用這個新函式必須要指派到一個變數上:
def uppercase_decorator(function):
return lambda: function().upper()
def greeting():
return "Welcome to Python"
g = uppercase_decorator(greeting)
print(g()) # WELCOME TO PYTHON
但透過裝飾器,我們不必新增一個函式,特別是,假如原本的函式已經在程式的各個地方中使用了,可以維持原本的函式名稱greeting
但擁有不一樣的回傳值:
def uppercase_decorator(function):
return lambda: function().upper()
@uppercase_decorator
def greeting():
return "Welcome to Python"
print(greeting()) # WELCOME TO PYTHON
套用多個裝飾器的話,輸出的順序會是由內而外:greeting
-> uppercase_decorator
-> split_string_decorator
def uppercase_decorator(function):
return lambda: function().upper()
def split_string_decorator(function):
return lambda: function().split()
@split_string_decorator
@uppercase_decorator
def greeting():
return "Welcome to Python"
print(greeting()) # ['WELCOME', 'TO', 'PYTHON']
split_string_decorator
會回傳出List,後續因為str.upper()
是String的方法,會產生AttributeError。這裡列出幾個內建的高階函數 -- map, filter, reduce,JavaScript中也有類似的函式。並且同樣的Python中這三個函式也是不可變的(immutable),不會影響到被遍歷的原始物件。
在Python 2.x,
map()
和filter()
回傳的是一個串列(list)。但在Python 3.x中他們分別會回傳map object和filter object,並且需要list()
函式來將他們轉為清單型別使用。
參照 What’s New In Python 3.0 — Python 3.10.7 documentation
語法: map(function, iterable, ...)
function
可以是外部宣告函式,也可以是給Lambda函式。...
的意思:如果function
接受多個參數,可以給多個iterable
參數,map
函式會遍歷各個iterable
參數,到長度最短的參數遍歷完後就會停止。# iterable
param1 = [1, 2, 3]
param2 = [4, 5]
# function
def square(x):
return x ** 2
numbers_squared = map(square, param1)
print(list(numbers_squared)) # [1, 4, 9]
"""
With a lambda function, which accepts two parameters.
Mapping index from 0 to 1 as the shortest length of the iterables is param2 which has 2 items inside.
"""
numbers_squared = map(lambda x, y: y ** x, param1, param2)
print(list(numbers_squared)) # [4, 25]
語法: filter(function, iterable)
這個函式會對參數iterable
中的每個項目執行function
,function
必須回傳True
或False
,執行時回傳True
的項目會被留下,False
則會被篩掉:
# iterable
numbers = [1, 2, 3, 4, 5]
def is_even(num):
if num % 2 == 0:
return True
return False
even_numbers = filter(is_even, numbers)
print(list(even_numbers)) # [2, 4]
語法: functools.reduce(function, iterable[, initializer])
這個函式是在functools
模組中定義的,需要引入才能使用,如果沒有給initializer
起始值參數,則會先取iterable
中第一個元素與第二個元素作為參數傳入function
中,該回傳值會接續做為第一個參數值傳入function
,第二個參數則為iterable
中第三個元素,以此類推,直到遍歷完整個物件回傳一個值:
from functools import reduce
numbers_str = ['1', '2', '3', '4', '5'] # iterable
def add_two_nums(x, y):
return int(x) + int(y)
total = reduce(add_two_nums, numbers_str)
print(total) # 15