
程序中反復(fù)執(zhí)行的代碼可以封裝到一個(gè)代碼塊中,這個(gè)代碼塊模仿了數(shù)學(xué)中的函數(shù),具有函數(shù)名、參數(shù)和返回值,這就是程序中的函數(shù)。
Python中的函數(shù)很靈活,它可以在模塊中,但類之外定義,即函數(shù),作用域是當(dāng)前模塊;也可以在別的函數(shù)中定義,即嵌套函數(shù);還可以在類中定義,即方法。
定義函數(shù)
在前面的學(xué)習(xí)過程中也用到了一些函數(shù),如果len()、min()和max(),這些函數(shù)都由Python官方提供的,稱為內(nèi)置函數(shù)(Built-in
Functions, 縮寫B(tài)IF)。
注意
Python作為解釋性語(yǔ)言函數(shù)必須先定義后調(diào)用,也就是定義函數(shù)必須在調(diào)用函數(shù)之前,否則會(huì)有錯(cuò)誤發(fā)生。
本節(jié)介紹自定義函數(shù),自定義函數(shù)的語(yǔ)法格式如下:
def 函數(shù)名(參數(shù)列表) :
函數(shù)體
return 返回值
在Python中定義函數(shù)時(shí),關(guān)鍵字是def,函數(shù)名需要符合標(biāo)識(shí)符命名規(guī)范;多個(gè)參數(shù)列表之間可以用逗號(hào)(,)分隔,當(dāng)然函數(shù)也可以沒有參數(shù)。如果函數(shù)有返回?cái)?shù)據(jù),就需要在函數(shù)體最后使用return語(yǔ)句將數(shù)據(jù)返回;如果沒有返回?cái)?shù)據(jù),則函數(shù)體中可以使用return
None或省略return語(yǔ)句。
函數(shù)定義示例代碼如下:
# coding=utf-8
# 代碼文件:chapter10/ch10.1.py
def rectangle_area(width, height): ①
area = width * height
return area ②
r_area = rectangle_area(320.0, 480.0) ③
print("320x480的長(zhǎng)方形的面積:{0:.2f}".format(r_area))
上述代碼第①行是定義計(jì)算長(zhǎng)方形面積的函數(shù)rectangle_area,它有兩個(gè)參數(shù),分別是長(zhǎng)方形的寬和高,width和height是參數(shù)名。代碼第②行代碼是通過return返回函數(shù)計(jì)算結(jié)果。代碼第③行是調(diào)用rectangle_area函數(shù)。
函數(shù)參數(shù)
Python中的函數(shù)參數(shù)很靈活,具體體現(xiàn)在傳遞參數(shù)有多種形式上。本節(jié)介紹幾種不同形式參數(shù)和調(diào)用方式。
使用關(guān)鍵字參數(shù)調(diào)用函數(shù)
為了提高函數(shù)調(diào)用的可讀性,在函數(shù)調(diào)用時(shí)可以使用關(guān)鍵字參數(shù)調(diào)用。采用關(guān)鍵字參數(shù)調(diào)用函數(shù),在函數(shù)定義時(shí)不需要做額外的工作。
示例代碼如下:
# coding=utf-8
# 代碼文件:chapter10/ch10.2.1.py
def print_area(width, height):
area = width * height
print("{0} x {1} 長(zhǎng)方形的面積:{2}".format(width, height, area))
print_area(320.0, 480.0) # 沒有采用關(guān)鍵字參數(shù)函數(shù)調(diào)用 ①
print_area(width=320.0, height=480.0) # 采用關(guān)鍵字參數(shù)函數(shù)調(diào)用②
print_area(320.0, height=480.0) # 采用關(guān)鍵字參數(shù)函數(shù)調(diào)用 ③
# print_area(width=320.0, height) # 發(fā)生錯(cuò)誤 ④
print_area(height=480.0, width=320.0) # 采用關(guān)鍵字參數(shù)函數(shù)調(diào)用 ⑤
print_area函數(shù)有兩個(gè)參數(shù),在調(diào)用時(shí)沒有采用關(guān)鍵字參數(shù)函數(shù)調(diào)用,見代碼第①行,也可以使用關(guān)鍵字參數(shù)調(diào)用函數(shù),見代碼第②行、第③行和第⑤行,其中width和height是參數(shù)名。從上述代碼比較可見采用關(guān)鍵字參數(shù)調(diào)用函數(shù),調(diào)用者能夠清晰地看出傳遞參數(shù)的含義,關(guān)鍵字參數(shù)對(duì)于有多參數(shù)函數(shù)調(diào)用非常有用。另外,采用關(guān)鍵字參數(shù)函數(shù)調(diào)用時(shí),參數(shù)順序可以與函數(shù)定義時(shí)參數(shù)順序不同。
注意
在調(diào)用函數(shù)時(shí),一旦其中一個(gè)參數(shù)采用了關(guān)鍵字參數(shù)形式傳遞,那么其后的所有參數(shù)都必須采用關(guān)鍵字參數(shù)形式傳遞。代碼第④行的函數(shù)調(diào)用中第一個(gè)參數(shù)width采用了關(guān)鍵字參數(shù)形式,而它后面的參數(shù)沒有采用關(guān)鍵字參數(shù)形式,因此會(huì)有錯(cuò)誤發(fā)生。
參數(shù)默認(rèn)值
在定義函數(shù)的時(shí)候可以為參數(shù)設(shè)置一個(gè)默認(rèn)值,當(dāng)調(diào)用函數(shù)的時(shí)候可以忽略該參數(shù)。來(lái)看下面的一個(gè)示例:
# coding=utf-8
# 代碼文件:chapter9/ch10.2.2.py
def make_coffee(name="卡布奇諾"):
return "制作一杯{0}咖啡。".format(name)
上述代碼定義了makeCoffee函數(shù),可以幫助我做一杯香濃的咖啡。由于我喜歡喝卡布奇諾,就把它設(shè)置為默認(rèn)值。在參數(shù)列表中,默認(rèn)值可以跟在參數(shù)類型的后面,通過等號(hào)提供給參數(shù)。
在調(diào)用的時(shí)候,如果調(diào)用者沒有傳遞參數(shù),則使用默認(rèn)值。調(diào)用代碼如下:
coffee1 = make_coffee("拿鐵") ①
coffee2 = make_coffee() ②
print(coffee1) # 制作一杯拿鐵咖啡。
print(coffee2) # 制作一杯卡布奇諾咖啡。
其中第①行代碼是傳遞"拿鐵"參數(shù),沒有使用默認(rèn)值。第②行代碼沒有傳遞參數(shù),因此使用默認(rèn)值。
提示
在其他語(yǔ)言中make_coffee函數(shù)可以采用重載實(shí)現(xiàn)多個(gè)版本。Python不支持函數(shù)重載,而是使用參數(shù)默認(rèn)值的方式提供類似函數(shù)重載的功能。因?yàn)閰?shù)默認(rèn)值只需要定義一個(gè)函數(shù)就可以了,而重載則需要定義多個(gè)函數(shù),這會(huì)增加代碼量。
可變參數(shù)
Python中函數(shù)的參數(shù)個(gè)數(shù)可以變化,它可以接受不確定數(shù)量的參數(shù),這種參數(shù)稱為可變參數(shù)。Python中可變參數(shù)有兩種,在參數(shù)前加*或**形式,*可變參數(shù)在函數(shù)中被組裝成為一個(gè)元組,**可變參數(shù)在函數(shù)中被組裝成為一個(gè)字典。
- *可變參數(shù)
下面看一個(gè)示例:
def sum(*numbers, multiple=1):
total = 0.0
for number in numbers:
total += number
return total * multiple
上述代碼定義了一個(gè)sum()函數(shù),用來(lái)計(jì)算傳遞給它的所有參數(shù)之和。*numbers是可變參數(shù)。在函數(shù)體中參數(shù)numbers被組裝成為一個(gè)元組,可以使用for循環(huán)遍歷numbers元組,計(jì)算他們的總和,然后返回給調(diào)用者。
下面是三次調(diào)用sum()函數(shù)的代碼:
print(sum(100.0, 20.0, 30.0)) # 輸出150.0
print(sum(30.0, 80.0)) # 輸出110.0
print(sum(30.0, 80.0, multiple=2)) # 輸出220.0 ①
double_tuple = (50.0, 60.0, 0.0) # 元組或列表 ②
print(sum(30.0, 80.0, *double_tuple)) # 輸出220.0 ③
可以看到,每次所傳遞參數(shù)的個(gè)數(shù)是不同的,前兩次調(diào)用時(shí)都省略了multiple參數(shù),第三次調(diào)用時(shí)傳遞了multiple參數(shù),此時(shí)multiple應(yīng)該使用關(guān)鍵字參數(shù)傳遞,否則有錯(cuò)誤發(fā)生。
如果已經(jīng)有一個(gè)元組變量(見代碼第②行),能否傳遞給可變參數(shù)呢?這需要使用對(duì)元組進(jìn)行拆包,見代碼第③行在元組double_tuple前面加上單星號(hào)(*),單星號(hào)在這里表示將double_tuple拆包為50.0,
60.0, 0.0形式。另外,double_tuple也可以是列表對(duì)象。
注意
*可變參數(shù)不是最后一個(gè)參數(shù)時(shí),后面的參數(shù)需要采用關(guān)鍵字參數(shù)形式傳遞。代碼第①行30.0,
80.0是可變參數(shù),后面multiple參數(shù)需要關(guān)鍵字參數(shù)形式傳遞。
- **可變參數(shù)
下面看一個(gè)示例:
def show_info(sep=':', **info):
print('-----info------')
for key, value in info.items():
print('{0} {2} {1}'.format(key, value, sep))
上述代碼定義了一個(gè)show_info()函數(shù),用來(lái)輸出一些信息,其中參數(shù)sep信息分隔符號(hào),默認(rèn)值是冒號(hào)(:)。**info是可變參數(shù),在函數(shù)體中參數(shù)info被組裝成為一個(gè)字典。
注意 **可變參數(shù)必須在正規(guī)參數(shù)之后,如果本例函數(shù)定義改為show_info(**info,sep=':')形式,會(huì)發(fā)生錯(cuò)誤。
下面是三次調(diào)用show_info()函數(shù)的代碼:
show_info('->', name='Tony', age=18, sex=True) ①
show_info(sutdent_name='Tony', sutdent_no='1000', sep='-') ②
stu_dict = {'name': 'Tony', 'age': 18} # 創(chuàng)建字典對(duì)象
show_info(**stu_dict, sex=True, sep='=') # 傳遞字典stu_dict ③
上述代碼第①行是調(diào)用函數(shù)show_info(),第一個(gè)參數(shù)'->'是傳遞給sep,其后的參數(shù)name='Tony',
age=18,
sex=True是傳遞給info,這種參數(shù)形式事實(shí)上就是關(guān)鍵字參數(shù),注意鍵不要用引號(hào)包裹起來(lái)。
代碼第②行是調(diào)用函數(shù)show_info(),sep也采用關(guān)鍵字參數(shù)傳遞,這種方式sep參數(shù)可以放置在參數(shù)列表的任何位置,其中的關(guān)鍵字參數(shù)被收集到info字典中。
代碼第③行是調(diào)用函數(shù)show_info(),其中字典對(duì)象stu_dict,傳遞時(shí)stu_dict前面加上雙星號(hào)(**),雙星號(hào)在這里表示將stu_dict拆包為key=value對(duì)的形式。
函數(shù)返回值
Python函數(shù)的返回值也是比較靈活的,主要有三種形式:無(wú)返回值、單一返回值和多返回值。前面使用的函數(shù)基本都單一返回值,本節(jié)重點(diǎn)介紹無(wú)返回值和多返回值兩種形式。
無(wú)返回值函數(shù)
有的函數(shù)只是為了處理某個(gè)過程,此時(shí)可以將函數(shù)設(shè)計(jì)為無(wú)返回值的。所謂無(wú)返回值,事實(shí)上是返回None,None表示沒有實(shí)際意義的數(shù)據(jù)。
無(wú)返回值函數(shù)示例代碼如下:
# coding=utf-8
# 代碼文件:chapter9/ch10.3.1.py
def show_info(sep=':', **info): ①
"""定義**可變參數(shù)函數(shù)"""
print('-----info------')
for key, value in info.items():
print('{0} {2} {1}'.format(key, value, sep))
return # return None 或省略 ②
result = show_info('->', name='Tony', age=18, sex=True)
print(result) # 輸出 None
def sum(*numbers, multiple=1): ③
"""定義*可變參數(shù)函數(shù)"""
if len(numbers) == 0:
return # return None 或省略 ④
total = 0.0
for number in numbers:
total += number
return total * multiple
print(sum(30.0, 80.0)) # 輸出110.0
print(sum(multiple=2)) # 輸出 None ⑥
上述代碼定義了兩個(gè)函數(shù),這個(gè)兩個(gè)函數(shù)事實(shí)上是在10.3.2節(jié)示例基礎(chǔ)上的重構(gòu)。其中代碼第①行的show_info()只是輸出一些信息,不需要返回?cái)?shù)據(jù),因此可以省略return語(yǔ)句。如果一定要使用return語(yǔ)句,見代碼第②行在函數(shù)結(jié)束前使用return或return
None。
對(duì)于本例中的show_info()函數(shù)強(qiáng)加return語(yǔ)句顯然是多此一舉,但是有時(shí)使用return或return
None是必要的。代碼第③行定義了sum()函數(shù),如果numbers中數(shù)據(jù)是空的,后面的求和計(jì)算也就沒有意義了,可以在函數(shù)的開始判斷numbers中算法有數(shù)據(jù),如果沒有數(shù)據(jù)則使用return或return
None跳出函數(shù),見代碼第④行。
多返回值函數(shù)
有時(shí)需要函數(shù)返回多個(gè)值,實(shí)現(xiàn)返回多個(gè)值的實(shí)現(xiàn)方式有很多,簡(jiǎn)單實(shí)現(xiàn)是使用元組返回多個(gè)值,因?yàn)樵M作為數(shù)據(jù)結(jié)構(gòu)可以容納多個(gè)數(shù)據(jù),另外元組是不可變的,使用起來(lái)比較安全。
下面來(lái)看一個(gè)示例:
# coding=utf-8
# 代碼文件:chapter9/ch10.3.2.py
def position(dt, speed): ①
posx = speed[0] * dt ②
posy = speed[1] * dt ③
return (posx, posy) ④
move = position(60.0, (10, -5)) ⑤
print("物體位移:({0}, {1})".format(move[0], move[1])) ⑥
這個(gè)示例是計(jì)算物體在指定時(shí)間和速度時(shí)的位移。第①行代碼是定義position函數(shù),其中dt參數(shù)是時(shí)間,speed參數(shù)是元組類型,speed第一個(gè)元素X軸上的速度,speed第二個(gè)元素Y軸上的速度。position函數(shù)的返回值也是元組類型。
函數(shù)體中的第②行代碼是計(jì)算X方向的位移,第③行代碼是計(jì)算Y方向的位移。最后,第④行代碼將計(jì)算后的數(shù)據(jù)返回,(posx,
posy)是元組類型實(shí)例。
第⑤行代碼調(diào)用函數(shù),傳遞的時(shí)間是60.0秒,速度是(10,
-5)。第⑥行代碼打印輸出結(jié)果,結(jié)果如下:
物體位移:(600.0, -300.0)
函數(shù)變量作用域
變量可以在模塊中創(chuàng)建,作用域是整個(gè)模塊,稱為全局變量。變量也可以在函數(shù)中創(chuàng)建,默認(rèn)情況下作用域是整個(gè)函數(shù),稱為局部變量。
示例代碼如下:
# coding=utf-8
# 代碼文件:chapter9/ch10.4.py
# 創(chuàng)建全局變量x
x = 20 ①
def print_value():
print("函數(shù)中x = {0}".format(x)) ②
print_value()
print("全局變量x = {0}".format(x))
輸出結(jié)果:
函數(shù)中x = 20
全局變量x = 20
上述代碼第①行是創(chuàng)建全局變量x,全局變量作用域是整個(gè)模塊,所以在print_value()函數(shù)中也可以訪問變量x,見代碼第②行。
修改上述示例代碼如下:
# 創(chuàng)建全局變量x
x = 20
def print_value():
# 創(chuàng)建局部變量x
x = 10 ①
print("函數(shù)中x = {0}".format(x))
print_value()
print("全局變量x = {0}".format(x))
輸出結(jié)果:
函數(shù)中x = 10
全局變量x = 20
上述代碼的是print_value()函數(shù)中添加x =
10語(yǔ)句,見代碼第①行,這會(huì)函數(shù)中創(chuàng)建x變量,函數(shù)中的x變量與全局變量x命名相同,在函數(shù)作用域內(nèi)會(huì)屏蔽全局x變量。
函數(shù)中創(chuàng)建的變量默認(rèn)作用域是當(dāng)前函數(shù),這可以防止程序員少犯錯(cuò)誤,因?yàn)楹瘮?shù)中創(chuàng)建的變量,如果作用域是整個(gè)模塊,那么在其他函數(shù)中也可以訪問,Python無(wú)法從語(yǔ)言層面定義常量,所以在其他函數(shù)中由于誤操作修改了變量,這樣一來(lái)很容易導(dǎo)致程序出現(xiàn)錯(cuò)誤。即便筆者不贊同,但Python提供這種可能,通過在函數(shù)中將變量聲明為global的,可以把變量的作用域變成全局的。
修改上述示例代碼如下:
# 創(chuàng)建全局變量x
x = 20
def print_value():
global x ①
x = 10 ②
print("函數(shù)中x = {0}".format(x))
print_value()
print("全局變量x = {0}".format(x))
輸出結(jié)果:
函數(shù)中x = 10
全局變量x = 10
代碼第①行是在函數(shù)中聲明x變量作用域?yàn)槿肿兞浚源a第②行修改x值,就是修改全局變量x的數(shù)值。
生成器
在一個(gè)函數(shù)中使用return關(guān)鍵字返回?cái)?shù)據(jù),但是有時(shí)候會(huì)使用yield 關(guān)鍵字返回?cái)?shù)據(jù)。yield 關(guān)鍵字的函數(shù)返回的是一個(gè)生成器(generator)對(duì)象,生成器對(duì)象是一種可迭代對(duì)象。
如果想計(jì)算平方數(shù)列,通常的實(shí)現(xiàn)代碼如下:
def square(num): ①
n_list = []
for i in range(1, num + 1):
n_list.append(i * i) ②
return n_list ③
for i in square(5): ④
print(i, end=' ')
返回結(jié)果是:
1 4 9 16 25
首先定義一個(gè)函數(shù),見代碼第①行。代碼第②行通過循環(huán)計(jì)算一個(gè)數(shù)的平方,并將結(jié)果保存到一個(gè)列表對(duì)象n_list中。最后返回列表對(duì)象,見代碼第③行。代碼第④行是遍歷返回的列表對(duì)象。
在Python中還可以有更加好解決方案,實(shí)現(xiàn)代碼如下:
def square(num):
for i in range(1, num + 1):
yield i * i ①
for i in square(5): ②
print(i, end=' ')
返回結(jié)果是:
1 4 9 16 25
代碼第①行使用了yield關(guān)鍵字返回平方數(shù),不再需要return關(guān)鍵字了。代碼第②行調(diào)用函數(shù)square()返回的是生成器對(duì)象。生成器對(duì)象是一種可迭代對(duì)象,可迭代對(duì)象通過next()方法獲得元素,代碼第②行的for循環(huán)能夠遍歷可迭代對(duì)象,就是隱式地調(diào)用生成器的next()方法獲得元素的。
顯式地調(diào)用生成器的next()方法,在Python Shell中運(yùn)行示例代碼如下:
>>> def square(num):
for i in range(1, num + 1):
yield i * i
>>> n_seq = square(5)
<generator object square at 0x000001C8F123CE60>
>>> n_seq.__next__() ①
1
>>> n_seq.__next__()
4
>>> n_seq.__next__()
9
>>> n_seq.__next__()
16
>>> n_seq.__next__()
25
>>> n_seq.__next__() ②
Traceback (most recent call last):
File "<pyshell#33>", line 1, in <module>
n_seq.__next__()
StopIteration
>>>
上述代碼第①行~第②行調(diào)用了6次next()方法,但第6次調(diào)用則會(huì)拋出StopIteration異常,這是因?yàn)橐呀?jīng)沒有元素可迭代了。
生成器函數(shù)是通過yield返回?cái)?shù)據(jù),與return不同的是:return語(yǔ)句一次返回所有數(shù)據(jù),函數(shù)調(diào)用結(jié)束;而yield語(yǔ)句只返回一個(gè)元素?cái)?shù)據(jù),函數(shù)調(diào)用不會(huì)結(jié)束,只是暫停,直到next()方法被調(diào)用,程序繼續(xù)執(zhí)行yield語(yǔ)句之后的語(yǔ)句代碼。這個(gè)過程如圖10-1所示。

注意
生成器特別適合用于遍歷一些大序列對(duì)象,它無(wú)須將對(duì)象的所有元素都載入內(nèi)存后才開始進(jìn)行操作。而是僅在迭代至某個(gè)元素時(shí)才會(huì)將該元素載入內(nèi)存。
嵌套函數(shù)
在本節(jié)之前定義的函數(shù)都是全局函數(shù),并將他們定義在全局作用域中。函數(shù)還可定義在另外的函數(shù)體中,稱作“嵌套函數(shù)”。
示例代碼:
# coding=utf-8
# 代碼文件:chapter10/ch10.6.py
def calculate(n1, n2, opr):
multiple = 2
# 定義相加函數(shù)
def add(a, b): ①
return (a + b) * multiple
# 定義相減函數(shù)
def sub(a, b): ②
return (a - b) * multiple
if opr == '+':
return add(n1, n2)
else:
return sub(n1, n2)
print(calculate(10, 5, '+')) # 輸出結(jié)果是30
# add(10, 5) 發(fā)生錯(cuò)誤 ③
# sub(10, 5) 發(fā)生錯(cuò)誤 ④
上述代碼中定義了兩個(gè)嵌套函數(shù):add()和sub(),見代碼第①行和第②行。嵌套函數(shù)可以訪問所在外部函數(shù)calculate()中的變量multiple,而外部函數(shù)不能訪問嵌套函數(shù)局部變量。另外,嵌套函數(shù)的作用域在外部函數(shù)體內(nèi),因此在外部函數(shù)體之外直接訪問嵌套函數(shù)會(huì)發(fā)生錯(cuò)誤,見代碼第③行和第④行。
函數(shù)式編程基礎(chǔ)
函數(shù)式編程(functional
programming)與面向?qū)ο缶幊桃粯佣家环N編程范式,函數(shù)式編程,也稱為面向函數(shù)的編程。
Python并不是徹底的函數(shù)式編程語(yǔ)言,但是還是提供了一些函數(shù)式編程必備的技術(shù),主要有:函數(shù)類型和Lambda表達(dá)式,他們是實(shí)現(xiàn)函數(shù)式編程的基礎(chǔ)。
函數(shù)類型
Python提供了一種函數(shù)類型function,任何一個(gè)函數(shù)都有函數(shù)類型,但是函數(shù)調(diào)用時(shí),就創(chuàng)建了函數(shù)類型實(shí)例,即函數(shù)對(duì)象。函數(shù)類型實(shí)例與其他類型實(shí)例一樣,在使用場(chǎng)景上沒有區(qū)別:它可以賦值給一個(gè)變量;也可以作為參數(shù)傳遞給一個(gè)函數(shù);還可以作為函數(shù)返回值使用。
為了理解函數(shù)類型,先重構(gòu)10.6節(jié)中嵌套函數(shù)的示例,示例代碼如下:
# coding=utf-8
# 代碼文件:chapter10/ch10.7.1.py
def calculate_fun(opr): ①
# 定義相加函數(shù)
def add(a, b):
return a + b
# 定義相減函數(shù)
def sub(a, b):
return a - b
if opr == '+':
return add ②
else:
return sub ③
f1 = calculate_fun('+') ④
f2 = calculate_fun('-') ⑤
print(type(f1))
print("10 + 5 = {0}".format(f1(10, 5))) ⑥
print("10 - 5 = {0}".format(f2(10, 5))) ⑦
輸出結(jié)果:
<class 'function'>
10 + 5 = 30
10 - 5 = 10
上述代碼第①行重構(gòu)了calculate_fun()函數(shù)的定義,現(xiàn)在只接收一個(gè)參數(shù)opr。代碼第②行是在opr
'+'為True時(shí)返回add函數(shù)名,否則返回sub函數(shù)名,見代碼第③行。這里的函數(shù)名本質(zhì)上函數(shù)對(duì)象。calculate_fun()函數(shù)與10.5節(jié)示例calculate()函數(shù)不同之處在于,calculate_fun()函數(shù)返回的是函數(shù)對(duì)象,calculate()函數(shù)返回的是整數(shù)(相加或相減計(jì)算之后的結(jié)果)。所以代碼第④行的f1是add()函數(shù)對(duì)象,代碼第⑤行的f2是sub()函數(shù)對(duì)象。
函數(shù)對(duì)象是可以與函數(shù)一樣進(jìn)行調(diào)用的。事實(shí)上在第⑥行之前沒有真正調(diào)用add()函數(shù)進(jìn)行相加計(jì)算,f1(10,
5)表達(dá)式才真的調(diào)用了add()函數(shù)。第⑦行的f2(10, 5)表達(dá)式是調(diào)用sub()函數(shù)。
Lambda表達(dá)式
理解了函數(shù)類型和函數(shù)對(duì)象,學(xué)習(xí)Lambda表達(dá)式就簡(jiǎn)單了。Lambda表達(dá)式本質(zhì)上一種匿名函數(shù),匿名函數(shù)也是函數(shù)有函數(shù)類型,也可以創(chuàng)建函數(shù)對(duì)象。
定義Lambda表達(dá)式語(yǔ)法如下:
lambda 參數(shù)列表 : Lambda體
lambda是關(guān)鍵字聲明這是一個(gè)Lambda表達(dá)式,“參數(shù)列表”與函數(shù)的參數(shù)列表是一樣的,但不需要小括號(hào)包裹起來(lái),冒號(hào)后面是“Lambda體”,Lambda表達(dá)式主要的代碼在此處編寫,類似于函數(shù)體。
注意
Lambda體部分不能是一個(gè)代碼塊,不能包含多條語(yǔ)句,只有一條語(yǔ)句,語(yǔ)句會(huì)計(jì)算一個(gè)結(jié)果返回給Lambda表達(dá)式,但是與函數(shù)不同是,不需要使用return語(yǔ)句返回。與其他語(yǔ)言中的Lambda表達(dá)式相比,Python中提供Lambda表達(dá)式只能處理一些簡(jiǎn)單的計(jì)算。
重構(gòu)10.7.1節(jié)示例,代碼如下:
# coding=utf-8
# 代碼文件:chapter10/ch10.7.2.py
def calculate_fun(opr):
if opr == '+':
return lambda a, b: (a + b) ①
else:
return lambda a, b: (a - b) ②
f1 = calculate_fun('+')
f2 = calculate_fun('-')
print(type(f1))
print("10 + 5 = {0}".format(f1(10, 5)))
print("10 - 5 = {0}".format(f2(10, 5)))
輸出結(jié)果:
<class 'function'>
10 + 5 = 30
10 - 5 = 10
上述代碼第①行替代了add()函數(shù),第②行替代了sub()函數(shù),代碼變的非常的簡(jiǎn)單。
三大基礎(chǔ)函數(shù)
函數(shù)式編程本質(zhì)是通過函數(shù)處理數(shù)據(jù),過濾、映射和聚合是處理數(shù)據(jù)的三大基本操作。針對(duì)但其中三大基本操作Python提供了三個(gè)基礎(chǔ)的函數(shù):filter()、map()和reduce()。
- filter()
過濾操作使用filter()函數(shù),它可以對(duì)可迭代對(duì)象的元素進(jìn)行過濾,filter()函數(shù)語(yǔ)法如下:
filter(function, iterable)
其中參數(shù)function是一個(gè)函數(shù),參數(shù)iterable是可迭代對(duì)象。filter()函數(shù)調(diào)用時(shí)iterable會(huì)被遍歷,它的元素逐一傳入function函數(shù),function函數(shù)返回布爾值,在function函數(shù)中編寫過濾條件,如果為True的元素被保留,如果為False的元素被過濾掉。
下面通過一個(gè)示例介紹一下filter()函數(shù)使用,示例代碼如下:
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_filter = filter(lambda u: u.startswith('T'), users) ①
print(list(users_filter))
輸出結(jié)果:
['Tony', 'Tom']
代碼第①行調(diào)用filter()函數(shù)過濾users列表,過濾條件是T開通的元素,lambda u:
u.startswith('T')是一個(gè)Lambda表達(dá)式,它提供了過濾條件。filter()函數(shù)還不是一個(gè)列表,需要使用list()函數(shù)轉(zhuǎn)換過濾之后的數(shù)據(jù)為列表。
再看一個(gè)示例:
number_list = range(1, 11)
nmber_filter = filter(lambda it: it % 2 == 0, number_list)
print(list(nmber_filter))
該示例實(shí)現(xiàn)了獲取1~10數(shù)字中的偶數(shù),輸出結(jié)果如下:
[2, 4, 6, 8, 10]
- map()
映射操作使用map()函數(shù),它可以對(duì)可迭代對(duì)象的元素進(jìn)行變換,map()函數(shù)語(yǔ)法如下:
map(function, iterable)
其中參數(shù)function是一個(gè)函數(shù),參數(shù)iterable是可迭代對(duì)象。map()函數(shù)調(diào)用時(shí)iterable會(huì)被遍歷,它的元素逐一傳入function函數(shù),在function函數(shù)中對(duì)元素進(jìn)行變換。
下面通過一個(gè)示例介紹一下map()函數(shù)使用,示例代碼如下:
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_map = map(lambda u: u.lower(), users) ①
print(list(users_map))
輸出結(jié)果:
['tony', 'tom', 'ben', 'alex']
上述代碼第①行調(diào)用map()函數(shù)將users列表元素轉(zhuǎn)換為小寫字母,變換使用Lambda表達(dá)式lambda
u:
u.lower()。map()函數(shù)返回的還不是一個(gè)列表,需要使用list()函數(shù)將過濾之后的數(shù)據(jù)轉(zhuǎn)換為列表。
函數(shù)式編程時(shí)數(shù)據(jù)可以從一個(gè)函數(shù)“流”入另外一個(gè)函數(shù),但是遺憾的是Python并不支持“鏈?zhǔn)健盇PI。例如想獲取users列表中“T”開通的名字,再將其轉(zhuǎn)換為小寫字母,這樣的需求需要使用filter()函數(shù)進(jìn)行過濾,再使用map()函數(shù)進(jìn)行映射變換。實(shí)現(xiàn)代碼如下:
users = ['Tony', 'Tom', 'Ben', 'Alex']
users_filter = filter(lambda u: u.startswith('T'), users)
# users_map = map(lambda u: u.lower(), users_filter) ①
users_map = map(lambda u: u.lower(), filter(lambda u: u.startswith('T'), users)) ②
print(list(users_map))
上述代碼第①行和第②行實(shí)現(xiàn)相同功能。
- reduce()
聚合操作會(huì)將多個(gè)數(shù)據(jù)聚合起來(lái)輸出單個(gè)數(shù)據(jù),聚合操作中最基礎(chǔ)的是歸納函數(shù)reduce(),reduce()函數(shù)會(huì)將多個(gè)數(shù)據(jù)按照指定的算法積累疊加起來(lái),最后輸出一個(gè)數(shù)據(jù)。
reduce()函數(shù)語(yǔ)法如下:
reduce(function, iterable[, initializer])
參數(shù)function是聚合操作函數(shù),該函數(shù)有兩個(gè)參數(shù),參數(shù)iterable是可迭代對(duì)象;參數(shù)initializer初始值。
下面通過一個(gè)示例介紹一下reduce()函數(shù)使用。下面示例實(shí)現(xiàn)了對(duì)一個(gè)數(shù)列求和運(yùn)算,代碼如下:
from functools import reduce ①
a = (1, 2, 3, 4)
a_reduce = reduce(lambda acc, i: acc + i, a) # 10 ②
# a_reduce = reduce(lambda acc, i: acc + i, a, 2) # 12 ③
print(a_reduce)
reduce()函數(shù)是在functools模塊中定義的,所以要使用reduce()函數(shù)需要導(dǎo)入functools模塊,見代碼第①行。代碼第②行是調(diào)用reduce()函數(shù),其中l(wèi)ambda
acc, i: acc +
i是進(jìn)行聚合操作的Lambda表達(dá)式,該Lambda表達(dá)式有兩個(gè)參數(shù),其中acc參數(shù)是上次累積計(jì)算結(jié)果,i當(dāng)前元素,acc
i表達(dá)式是進(jìn)行累加。reduce()函數(shù)最后的計(jì)算結(jié)果是一個(gè)數(shù)值,直接可以使用通過reduce()函數(shù)返回。代碼第行是傳入了初始值2,則計(jì)算的結(jié)果是12。
本章小結(jié)
通過對(duì)本章內(nèi)容的學(xué)習(xí),讀者可以熟悉在Python中如何定義函數(shù)、函數(shù)參數(shù)和函數(shù)返回值,了解函數(shù)變量作用域和嵌套函數(shù)。最后還介紹了Python中函數(shù)式編程基礎(chǔ)。
配套視頻
http://www.zhijieketang.com/classroom/10/courses
配套源代碼
http://www.zhijieketang.com/group/8
紙質(zhì)版電商
京東:https://item.jd.com/12468732.html
當(dāng)當(dāng):http://product.dangdang.com/25574315.html
作者微博:@tony_關(guān)東升
郵箱:eorient@sina.com
智捷課堂微信公共號(hào):zhijieketang
Python讀者服務(wù)QQ群:565736812
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 原文鏈接:https://github.com/EasyKotlin 值就是函數(shù),函數(shù)就是值。所有函數(shù)都消費(fèi)函數(shù),...
- 第5章 函數(shù)和函數(shù)式編程 5.1 引言函數(shù)是組織好的,可重復(fù)使用的,用來(lái)實(shí)現(xiàn)單一,或相關(guān)聯(lián)功能的代碼段。函數(shù)...