iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
自我挑戰組

從前端角度看30天學Python系列 第 14

【Day 14】高階函式

  • 分享至 

  • xImage
  •  
  • 閉包(closures)
  • 裝飾器(decorators)
  • Python內建的高階函數
    • Map函式
    • Filter函式
    • Reduce函式

這篇文章是閱讀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

閉包(closures)

概念跟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

裝飾器(decorators)

就原文所說,這是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。

Python內建的高階函數

這裡列出幾個內建的高階函數 -- 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函式

語法: 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函式

語法: filter(function, iterable)

這個函式會對參數iterable中的每個項目執行functionfunction必須回傳TrueFalse,執行時回傳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]

Reduce函式

語法: 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

上一篇
【Day 13】列表推導式
下一篇
【Day 15】Python中的型別錯誤
系列文
從前端角度看30天學Python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言