一級函式、lambda 運算式

April 20, 2022

在 Python 中,函式不單只是個定義,還是個值,被定義的函式會產生函式物件,它是 function 的實例。

一級函式

既然函式是物件,也就可以指定給其他的變數。例如:

>>> def max(num1, num2):
...     return num1 if num1 > num2 else num2
...
>>> maximum = max
>>> maximum(10, 5)
10
>>> type(max)
<class 'function'>
>>>

上面在定義了 max 函式之後,透過 max 名稱將函式物件指定給 maximum 名稱,無論透過 max 或者 maximum(10, 5),結果都是呼叫了它們參考的函式物件。

函式跟數值、listsetdicttuple 等一樣,都被 Python 視為一級公民來對待,可以自由地在變數、函式呼叫時指定,因此具有這樣特性的函式,也被稱一級函式(First-class function),函式代表著某個可重用流程的封裝,當它可以作為值傳遞時,就表示可以將某個可重用流程進行傳遞,這是個極具威力的功能。

來看個一級函式傳遞的例子,到目前為止,經常會使用 listtuple 等有序結構,有時會想排序其中的元素,這時可以使用 sorted 函式,它可以針對你指定的方式進行排序。例如:

>>> sorted([2, 1, 3, 6, 5])
[1, 2, 3, 5, 6]
>>> sorted([2, 1, 3, 6, 5], reverse = True)
[6, 5, 3, 2, 1]
>>> sorted(('Justin', 'openhome', 'momor'), key = len)
['momor', 'Justin', 'openhome']
>>> sorted(('Justin', 'openhome', 'momor'), key = len, reverse = True)
['openhome', 'Justin', 'momor']
>>>

sorted 會傳回新的 list,其中包含了排序後的結果,key 參數可用來指定針對什麼特性來迭代,例如在指定 len 函式時,每個元素都會傳入 len 運算,得到的長度值再作為排序依據。

如果是可變動的 list,本身也有個 sort 方法,這個方法會直接在list本身排序,不像 sorted 方法會傳回新的 list。例如:

>>> lt = [2, 1, 3, 6, 5]
>>> lt.sort()
>>> lt
[1, 2, 3, 5, 6]
>>> lt.sort(reverse = True)
>>> lt
[6, 5, 3, 2, 1]
>>> names = ["Justin", "openhome", "momor"]
>>> names.sort(key = len)
>>> names
['momor', 'Justin', 'openhome']
>>>

lambda 運算式

在之前的排序範例中,直接使用了 len 函式,依長度來排序,如果想針對最後一個字母排序呢?

>>> def last(name):
...     return name[-1]
... 
>>> sorted(('Justin', 'openhome', 'momor'), key = last) 
['openhome', 'Justin', 'momor']
>>>

函式本體只是個簡單的運算式,使用 def 顯得有點大費周章了,如果函式本體很簡單,只有一句簡單的運算,對於這類情況,可以考慮使用 lambda 運算式。例如:

>>> sorted(('Justin', 'openhome', 'momor'), key = lambda name: name[-1]) 
['openhome', 'Justin', 'momor']
>>>

lambda 關鍵字之後定義的是參數,而冒號 : 之後定義的是函式本體,運算結果會作為傳回值,不需要加上 return,像 lambda name: name[-1] 這樣的 lambda 運算式會建立 function 實例,也就是一個函式,有時臨時只是需要個小函式,使用 lambda 就很方便。

如果 lambda 不需要參數,直接在 lambda 後加上冒號就可以了,若需要兩個以上的參數,中間要使用逗號 , 區隔。例如:

>>> max = lambda n1, n2: n1 if n1 > n2 else n2
>>> max(10, 5)
10
>>>

相較於其他語言中的 lambda 語法,Python 使用 lambda 關鍵字的方式,其實並不簡潔,甚至有點妨礙可讀性, Python 的 lambda 也沒辦法寫太複雜的邏輯,這是 Python 為了避免 lambda 被濫用而特意做的限制,如果覺得可讀性不佳,或者需要撰寫更複雜的邏輯,請乖乖地使用 def 定義函式,並給予一個清楚易懂的函式名稱。

在 Python 中,會看到不少 API 可以接受函式,不過嚴格來說,是接受 callable 物件,也就是行為上像函式可以呼叫的物件,具體而言,就是實作了 __call__ 方法的物件,從 API 設計者角度來看,一個 callable 物件是以函式或類別定義並不重要,這讓 API 在使用上可以有更多的彈性。

分享到 LinkedIn 分享到 Facebook 分享到 Twitter