Python裏的匿名函數(lambda)與apply(),filter() ,map(),reduce(),以及函數的可變長參數

lambda:

提到 Lambda演算,更多時候是與函數式編程糾纏在一塊兒的。這種設計思想講究拋棄變量和狀態,使用純函數的遞歸系統來構建程序(我的理解)。雖然函數式編程與 Python 的面向對象背道而馳,但並不妨害 Python 借鑑其中某些有價值的內容。便是說,並不能由於 lambda 的存在就認爲 Python 是一門函數式編程語言,它只是由於在某些細節上顯得更有效率而被引入的。好比 Python 裏用 lambda 來定義匿名函數,並與標題中提到的 apply() 等內建函數一塊兒構建一些程序結構。python

匿名函數與標準方式聲明的函數區別在於,不須要使用 def 語句,也不須要一個名字來引用它。使用 lambda 語句能夠直接獲得一個函數對象,它的語法是:express

lambda [arg1[,arg2…]]: expression編程

參數無關緊要,冒號後面是一個表達式,函數的做用就是返回這個表達式的值。在 def 語句下等同於:app

def func([arg1[,arg2…]]):return expression編程語言

能夠看到 lambda 函數沒有中間狀態,也不適合構建過於複雜的函數,由於它的函數體只有一個表達式。因此應用 lambda 的場合更可能是構建一些臨時的、簡單的和無需複用小函數。雖然原則上能夠給 lambda 函數起別名,就像下面這樣,以備後續引用。但若是你打算重複利用這個函數,幹嗎不用 def 語句定義一個標準函數呢,那樣功能更多且更易維護。函數式編程

>>> foo = lambda a,b: a+b
>>> foo(1,2)
3

filter(), map(), reduce():

apply() 函數由於涉及到可變長參數,而且與這三個函數的做用不一樣,所以放到最後。filter(), map(), reduce() 這三個內建函數的功能屬於一類,都是處理可迭代對象,而後返回結果。函數

filter(function or None, iterable) --> filter object 的功能就和它的名字同樣,使用可迭代對象 iterable 中的每個元素做爲參數來調用布爾函數 function,並將全部返回 True 的元素放在一個迭代器中返回。若是 function 爲 None,則將 iterable 中值爲 True 的元素返回。例:設計

>>> a = filter(lambda x:x>2,[1,2,3,4])
>>> type(a)
<class 'filter'>
>>> for i in a:print(i)

3
4
>>>

在之前的版本中 filter() 函數返回的是一個列表,但 Python3 改成了返回一個迭代器,因此上面的例子使用了 for 語句來顯示結果。另外返回迭代器的一個注意點就是:上例中的 a 中的元素並非在賦值的時候一次性生成的,所以若是使用 iterable 的元素調用 function 會產生異常的話,那麼該異常實際會在 for 循環中被拋出。能夠看到在這種「過濾元素」的應用環境中,咱們須要的函數形式很是簡單,只有一條表達式,因此使用 lambda 是個不錯的選擇。(實際下面的兩個函數也是同樣)3d

map(func, *iterables) --> map object 的功能是將函數 func 做用於 iterable 的每個元素,並將結果用迭代器返回。注意這裏能夠提供多個 iterable,若是這樣作,調用 func 的時候就會從每一個 iterable 中依次取一個元素,直到最短的 iterable 耗盡。因此這裏 func 的參數個數應該等於 iterable 的個數。code

>>> a = map(lambda x,y:x+y,[1,2,3],[3,2,1])
>>> type(a)
<class 'map'>
>>> for i in a:print(i)

4
4
4

reduce(function, sequence[, initial]) –> value 的功能是:function 接受兩個參數,第一個是 initial,第二個是 sequence 的第一個元素。若是沒有提供 initial ,那麼就取前兩個 sequence 的元素。而後此次調用的返回值再做爲第一個參數傳遞給 function,第二個參數則是 sequence 的下一個元素。這樣循環直到 sequence 耗盡,並將最終的值返回。由於 reduce() 函數在 Python3 中已經不是內建函數了,想要使用的話須要 from functools import reduce,因此這裏就不舉例了。

實際上不僅 reduce,這三個函數在如今的版本中均可以說已經沒什麼用了。filter() 和 map() 均可以由列表解析(生成器表達式)取代,並且明顯列表解析更高級好用。因此通常知道這幾個函數是幹嘛用的就夠了,基本不必去實際使用他們。不過下面用來取代 map() 的列表解析示例貌似只能用於單容器的情況,多容器的好比上面那個 map() 的例子該怎麼寫我想不出。。。

>>> [x for x in [1,2,3,4] if x>2]# filter
[3, 4]
>>> [x+1 for x in [1,2,3]]# map
[2, 3, 4]

apply() 比 reduce() 還慘,在 functools 裏都已經不存在了。這是由於早在 1.6 版本他就已經失去做用。咱們這裏還要提它,僅是爲了引出下面的話題:可變長參數。其實在 1.6 之前,函數還不支持可變長參數,而 apply() 就是幹這個用的,他可使用可變長參數來調用函數。

可變長參數:

可變長參數的意思就是:我在調用函數的時候不知道要傳多少個參數進來,可能沒有,也可能 100 多個。好比一張電子帳單,算總額的時候誰知道會有多少項呢。相似這種問題雖然有不少種其餘方法把金額加到一塊兒,但使用單一函數來實現總能收穫到多餘的好處。並且雖然用一個元組也能夠實現變長參數的功能,但接下來介紹的方式能作的更好。定義一個函數時可以使用的形參徹底體以下:

def func(positional_args, keyword_args, *tuple_grp_nonkw_args, **dict_grp_kw_args):

這四種參數形式都是獨立可選的。前兩個不表,後面帶個 * 和帶 ** 的就是變長參數了。在位置上,帶 * 的必須放在不帶 * 的後面,不然會報錯。舉個栗子,operator 模塊裏有個 add() 函數:

add(a, b) -- Same as a + b.

該函數只接受兩個參數,若是使用可變長參數的話,咱們就能讓它接受無限個參數並返回他們的和:

>>> def add(*args):
	result = 0
	for num in args:
		result += num
	return result

>>> add(1,2,3,4)
10
>>> add(*(1,2,3,4))
10
>>> add(1,2,*(3,4))
10

從上面的例子能夠看出 *tuple_grp_nonkw_args 的做用。只要你的形參裏有 *args 這種設定,函數就自動變得對非關鍵字參數來者不拒。並把接收到的全部參數統一放在一個元組裏。

不過這個函數較 operator 裏的 add() 有個不足之處,就是那個 add() 除了加數字,還能鏈接字符串,而咱們定義的這個就只能處理數字。因而,接下來就要把位置參數(positional_args)和可變長非關鍵字參數(*tuple_grp_nonkw_args)一塊兒用了,這也是能體現他們之間聯繫的一個例子:

>>> def add(first,*args):
	result = first
	checked = type(first)
	for item in [x for x in args if type(x)==checked]:
		result += item
	return result

>>> add(1,2,3,4)
10
>>> add('1','2','3','4')
'1234'
>>> add(1,2,'3','4')
3
>>> add(*(1,2,3,4))
10

這裏咱們從 *args 裏獨立出來了一個非關鍵字參數,並把它命名爲 first,再以後的參數只有和 first 同類型的纔會加在一塊兒。能夠看到這時的 *args 自動讓出了第一個參數給 first,而只包含剩餘的參數,不論你是單獨傳入第一個參數,仍是用 *()的方式一股腦傳入,函數內部對待他們的方式都是同樣的。

忽然想到上面的例子,int 和 float 參數仍是加不到一塊兒。因此能夠改一下斷定:if type(x) in (int,float) 這樣。

而關鍵字參數的表現和非關鍵字的同樣,這裏就不贅述了。

相關文章
相關標籤/搜索