作者 | 弗拉德
來源 | 弗拉德(公眾號(hào):fulade_me)
定義函數(shù)
下面是一個(gè)打印問候語的簡單函數(shù),名為greet_user():
def greet_user():
"""顯示簡單的問候語"""
print("Hello!")
greet_user()
這個(gè)示例演示了最簡單的函數(shù)結(jié)構(gòu)。使用關(guān)鍵字def
來告訴Python你要定義一個(gè)
函數(shù)。在這里,函數(shù)名為greet_user()
,它不需要任何信息就能完成其工作,因此括號(hào)是空的(即便如此,括號(hào)也必不可少)。最后,定義以冒號(hào)結(jié)尾。
緊跟在def greet_user()
:后面的所有縮進(jìn)行構(gòu)成了函數(shù)體。代碼行print("Hello!"
)是函數(shù)體內(nèi)的唯一一行代碼,greet_user()
只做一項(xiàng)工作:打印Hello!。
由于這個(gè)函數(shù)不需要任何參數(shù),因此調(diào)用它時(shí)只需輸入greet_user()
即可。和預(yù)期的一樣,它打印Hello!
。
向函數(shù)傳遞信息
只需稍作修改,就可以讓函數(shù)greet_user()
不僅向用戶顯示Hello!
,還將用戶的名字用作參數(shù)。
因此,可在函數(shù)定義def greet_user()
的括號(hào)內(nèi)添加username
。通過在這里添加username
,就可讓函數(shù)接受你給username
指定的任何值。現(xiàn)在,這個(gè)函數(shù)要求你調(diào)用它時(shí)給username
指定一個(gè)值。調(diào)用greet_user()
時(shí),可將一個(gè)名字傳遞給它,如下所示:
def greet_user(username):
"""顯示簡單的問候語"""
print("Hello, " + username.title() + "!")
greet_user('Fulade')
代碼greet_user('Fulade')
調(diào)用函數(shù)greet_user()
,并向它提供執(zhí)行print
語句所需的信息。這個(gè)函數(shù)接受你傳遞給它的名字,并向這個(gè)人發(fā)出問候:
Hello Fulade !
同樣,greet_user('sarah')
調(diào)用函數(shù)greet_user()
并向它傳遞sarah
,打印Hello, Sarah!
。
你可以根據(jù)需要調(diào)用函數(shù)greet_user()
任意次,調(diào)用時(shí)無論傳入什么樣的名字,都會(huì)生成相應(yīng)的輸出。
實(shí)參和形參
前面定義函數(shù)greet_user()
時(shí),要求給變量username
指定一個(gè)值。調(diào)用這個(gè)函數(shù)并提供這種參數(shù),它將打印相應(yīng)的問候語。
在函數(shù)greet_user()
的定義中,變量username
是一個(gè)形參——函數(shù)完成其工作所需的一項(xiàng)信息。在代碼greet_user('Fulade')
中,值Fulade
是一個(gè)實(shí)參。
實(shí)參是調(diào)用函數(shù)時(shí)傳遞給函數(shù)的信息。我們調(diào)用函數(shù)時(shí),將要讓函數(shù)使用的信息放在括號(hào)內(nèi)。在greet_user('Fulade')
中,將實(shí)參Fulade
傳遞給了函數(shù)greet_user()
,這個(gè)值被存儲(chǔ)在形參username
中。
傳遞實(shí)參
鑒于函數(shù)定義中可能包含多個(gè)形參,因此函數(shù)調(diào)用中也可能包含多個(gè)實(shí)參。向函數(shù)傳遞實(shí)參的方式很多,可使用位置實(shí)參,這要求實(shí)參的順序與形參的順序相同;也可使用關(guān)鍵字實(shí)參,其中每個(gè)實(shí)參都由變量名和值組成;還可使用列表和字典。下面來依次介紹這些方式。
位置實(shí)參
你調(diào)用函數(shù)時(shí),Python必須將函數(shù)調(diào)用中的每個(gè)實(shí)參都關(guān)聯(lián)到函數(shù)定義中的一個(gè)形參。因此,最簡單的關(guān)聯(lián)方式是基于實(shí)參的順序。這種關(guān)聯(lián)方式被稱為位置實(shí)參。為明白其中的工作原理,來看一個(gè)顯示寵物信息的函數(shù)。這個(gè)函數(shù)指出一個(gè)寵物屬于哪種動(dòng)物以及它叫什么名字,如下所示:
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('hamster', 'harry')
這個(gè)函數(shù)的定義表明,它需要一種動(dòng)物類型和一個(gè)名字。調(diào)用describe_pet()
時(shí),需要按順序提供一種動(dòng)物類型和一個(gè)名字。
例如,在前面的函數(shù)調(diào)用中,實(shí)參hamster
存儲(chǔ)在形參animal_type
中,而實(shí)參harry
存儲(chǔ)在形參pet_name
中。在函數(shù)體內(nèi),使用了這兩個(gè)形參來顯示寵物的信息。
調(diào)用函數(shù)多次
你可以根據(jù)需要調(diào)用函數(shù)任意次。要再描述一個(gè)寵物,只需再次調(diào)用describe_pet()
即可:
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('hamster', 'harry')
describe_pet('dog', 'willie')
第二次調(diào)用describe_pet()
函數(shù)時(shí),我們向它傳遞了實(shí)參dog
和willie
。與第一次調(diào)用時(shí)一樣,Python將實(shí)參dog
關(guān)聯(lián)到形參animal_type
,并將實(shí)參willie
關(guān)聯(lián)到形參pet_name
。
與前面一樣,這個(gè)函數(shù)完成其任務(wù),但打印的是一條名為Willie
的小狗的信息。至此,我們有一只名為Harry
的倉鼠,還有一條名為Willie
的小狗:
I have a hamster.
My hamster's name is Harry.
I have a dog.
My dog's name is Willie.
調(diào)用函數(shù)多次是一種效率極高的工作方式。我們只需在函數(shù)中編寫描述寵物的代碼一次,然后每當(dāng)需要描述新寵物時(shí),都可調(diào)用這個(gè)函數(shù),并向它提供新寵物的信息。即便描述寵物的代碼增加到了10行,你依然只需使用一行調(diào)用函數(shù)的代碼,就可描述一個(gè)新寵物。
在函數(shù)中,可根據(jù)需要使用任意數(shù)量的位置實(shí)參,Python將按順序?qū)⒑瘮?shù)調(diào)用中的實(shí)參關(guān)聯(lián)到函數(shù)定義中相應(yīng)的形參。
位置實(shí)參的順序很重要
使用位置實(shí)參來調(diào)用函數(shù)時(shí),如果實(shí)參的順序不正確,結(jié)果可能出乎意料:
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('harry', 'hamster')
在這個(gè)函數(shù)調(diào)用中,我們先指定名字,再指定動(dòng)物類型。由于實(shí)參harry
在前,這個(gè)值將存儲(chǔ)到形參animal_type
中;同理,hamster
將存儲(chǔ)到形參pet_name
中。結(jié)果是我們得到了一個(gè)名為Hamster
的harry
:
I have a harry.
My harry's name is Hamster.
如果結(jié)果像上面一樣搞笑,請(qǐng)確認(rèn)函數(shù)調(diào)用中實(shí)參的順序與函數(shù)定義中形參的順序一致。
關(guān)鍵字實(shí)參
關(guān)鍵字實(shí)參是傳遞給函數(shù)的名稱—值對(duì)。你直接在實(shí)參中將名稱和值關(guān)聯(lián)起來了,因此向函數(shù)傳遞實(shí)參時(shí)不會(huì)混淆。
關(guān)鍵字實(shí)參讓你無需考慮函數(shù)調(diào)用中的實(shí)參順序,還清楚地指出了函數(shù)調(diào)用中各個(gè)值的用途。下面來重新編寫pets.py
,在其中使用關(guān)鍵字實(shí)參來調(diào)用describe_pet()
:
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(animal_type='hamster', pet_name='harry')
函數(shù)describe_pet()
還是原來那樣,但調(diào)用這個(gè)函數(shù)時(shí),我們向Python明確地指出了各個(gè)實(shí)參對(duì)應(yīng)的形參。看到這個(gè)函數(shù)調(diào)用時(shí),Python知道應(yīng)該將實(shí)參hamster
和harry
分別存儲(chǔ)在形參animal_type
和pet_name
中。輸出正確無誤,它指出我們有一只名為Harry
的倉鼠。
關(guān)鍵字實(shí)參的順序無關(guān)緊要,因?yàn)镻ython知道各個(gè)值該存儲(chǔ)到哪個(gè)形參中。下面兩個(gè)函數(shù)調(diào)用是等效的:
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster')
默認(rèn)值
編寫函數(shù)時(shí),可給每個(gè)形參指定默認(rèn)值。在調(diào)用函數(shù)中給形參提供了實(shí)參時(shí),Python將使用指定的實(shí)參值;否則,將使用形參的默認(rèn)值。因此,給形參指定默認(rèn)值后,可在函數(shù)調(diào)用中省略相應(yīng)的實(shí)參。使用默認(rèn)值可簡化函數(shù)調(diào)用,還可清楚地指出函數(shù)的典型用法。
例如,如果你發(fā)現(xiàn)調(diào)用describe_pet()
時(shí),描述的大都是小狗,就可將形參animal_type
的默認(rèn)值設(shè)置為dog
。這樣,調(diào)用describe_pet()
來描述小狗時(shí),就可不提供這種信息:
def describe_pet(pet_name, animal_type='dog'):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(pet_name='willie')
這里修改了函數(shù)describe_pet()
的定義,在其中給形參animal_type
指定了默認(rèn)值dog
。這樣,調(diào)用這個(gè)函數(shù)時(shí),如果沒有給animal_type
指定值,Python將把這個(gè)形參設(shè)置為dog
:
I have a dog.
My dog's name is Willie.
請(qǐng)注意,在這個(gè)函數(shù)的定義中,修改了形參的排列順序。由于給animal_type
指定了默認(rèn)值,無需通過實(shí)參來指定動(dòng)物類型,因此在函數(shù)調(diào)用中只包含一個(gè)實(shí)參——寵物的名字。然而,Python依然將這個(gè)實(shí)參視為位置實(shí)參,因此如果函數(shù)調(diào)用中只包含寵物的名字,這個(gè)實(shí)參將關(guān)聯(lián)到函數(shù)定義中的第一個(gè)形參。這就是需要將pet_name放在形參列表開頭的原因所在。
現(xiàn)在,使用這個(gè)函數(shù)的最簡單的方式是,在函數(shù)調(diào)用中只提供小狗的名字:
describe_pet('willie')
這個(gè)函數(shù)調(diào)用的輸出與前一個(gè)示例相同。只提供了一個(gè)實(shí)參——willie
,這個(gè)實(shí)參將關(guān)聯(lián)到函數(shù)定義中的第一個(gè)形參——pet_name
。由于沒有給animal_type
提供實(shí)參,因此Python使用其默認(rèn)值dog
。
如果要描述的動(dòng)物不是小狗,可使用類似于下面的函數(shù)調(diào)用:
describe_pet(pet_name='harry', animal_type='hamster')
由于顯式地給animal_type
提供了實(shí)參,因此Python將忽略這個(gè)形參的默認(rèn)值。
等效的函數(shù)調(diào)用
由于可混合使用位置實(shí)參、關(guān)鍵字實(shí)參和默認(rèn)值,通常有多種等效的函數(shù)調(diào)用方式。請(qǐng)看下
面的函數(shù)describe_pets()
的定義,其中給一個(gè)形參提供了默認(rèn)值:
def describe_pet(pet_name, animal_type='dog'):
基于這種定義,在任何情況下都必須給pet_name
提供實(shí)參;指定該實(shí)參時(shí)可以使用位置方式,也可以使用關(guān)鍵字方式。如果要描述的動(dòng)物不是小狗,還必須在函數(shù)調(diào)用中給animal_type
提供實(shí)參;同樣,指定該實(shí)參時(shí)可以使用位置方式,也可以使用關(guān)鍵字方式。
下面對(duì)這個(gè)函數(shù)的所有調(diào)用都可行:
# 一條名為Willie的小狗
describe_pet('willie')
describe_pet(pet_name='willie')
# 一只名為Harry的倉鼠
describe_pet('harry', 'hamster')
describe_pet(pet_name='harry', animal_type='hamster')
describe_pet(animal_type='hamster', pet_name='harry')
這些函數(shù)調(diào)用的輸出與前面的示例相同。
避免實(shí)參錯(cuò)誤
等你開始使用函數(shù)后,如果遇到實(shí)參不匹配錯(cuò)誤,不要大驚小怪。你提供的實(shí)參多于或少于函數(shù)完成其工作所需的信息時(shí),將出現(xiàn)實(shí)參不匹配錯(cuò)誤。
例如,如果調(diào)用函數(shù)describe_pet()
時(shí)沒有指定任何實(shí)參,結(jié)果將如何呢?
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet()
Python發(fā)現(xiàn)該函數(shù)調(diào)用缺少必要的信息,而traceback
指出了這一點(diǎn):
Traceback (most recent call last):
File "pets.py", line 6, in <module>
describe_pet()
TypeError: describe_pet() missing 2 required positional arguments: 'animal_
type' and 'pet_name'
返回值
函數(shù)并非總是直接顯示輸出,相反,它可以處理一些數(shù)據(jù),并返回一個(gè)或一組值。函數(shù)返回的值被稱為返回值。在函數(shù)中,可使用return
語句將值返回到調(diào)用函數(shù)的代碼行。返回值讓你能夠?qū)⒊绦虻拇蟛糠址敝毓ぷ饕频胶瘮?shù)中去完成,從而簡化主程序。
返回簡單值
下面來看一個(gè)函數(shù),它接受名和姓并返回姓名:
def get_formatted_name(first_name, last_name):
"""返回的姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
函數(shù)get_formatted_name()
的定義通過形參接受名和姓。它將姓和名合而為一,在它們之間加上一個(gè)空格,并將結(jié)果存儲(chǔ)在變量full_name
中。然后,將full_name
的值轉(zhuǎn)換為首字母大寫格式,并將結(jié)果返回到函數(shù)調(diào)用行。調(diào)用返回值的函數(shù)時(shí),需要提供一個(gè)變量,用于存儲(chǔ)返回的值。
在這里,將返回值存儲(chǔ)在了變量musician
中。輸出是姓名:
Jimi Hendrix
讓實(shí)參變成可選的
有時(shí)候,需要讓實(shí)參變成可選的,這樣使用函數(shù)的人就只需在必要時(shí)才提供額外的信息。可使用默認(rèn)值來讓實(shí)參變成可選的。
例如,假設(shè)我們要擴(kuò)展函數(shù)get_formatted_name()
,使其還處理中間名。為此,可將其修改成類似于下面這樣:
def get_formatted_name(first_name, middle_name, last_name):
"""返回姓名"""
full_name = first_name + ' ' + middle_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('john', 'lee', 'hooker')
print(musician)
只要同時(shí)提供名、中間名和姓,這個(gè)函數(shù)就能正確地運(yùn)行。它根據(jù)這三部分創(chuàng)建一個(gè)字符串,在適當(dāng)?shù)牡胤郊由峡崭瘢⒔Y(jié)果轉(zhuǎn)換為首字母大寫格式:
John Lee Hooker
然而,并非所有的人都有中間名,但如果你調(diào)用這個(gè)函數(shù)時(shí)只提供了名和姓,它將不能正確地運(yùn)行。為讓中間名變成可選的,可給實(shí)參middle_name
指定一個(gè)默認(rèn)值——空字符串,并在用戶沒有提供中間名時(shí)不使用這個(gè)實(shí)參。
為讓get_formatted_name()
在沒有提供中間名時(shí)依然可行,可給實(shí)參middle_name
指定一個(gè)默認(rèn)值——空字符串,并將其移到形參列表的末尾:
def get_formatted_name(first_name, last_name, middle_name=''):
"""返回姓名"""
if middle_name:
full_name = first_name + ' ' + middle_name + ' ' + last_name
else:
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)
在這個(gè)示例中,姓名是根據(jù)三個(gè)可能提供的部分創(chuàng)建的。由于人都有名和姓,因此在函數(shù)定義中首先列出了這兩個(gè)形參。中間名是可選的,因此在函數(shù)定義中最后列出該形參,并將其默認(rèn)值設(shè)置為空字符串。
在函數(shù)體中,我們檢查是否提供了中間名。Python將非空字符串解讀為True,因此如果函數(shù)調(diào)用中提供了中間名,if middle_name
將為True
。如果提供了中間名,就將名、中間名和姓合并為姓名,然后將其修改為首字母大寫格式,并返回到函數(shù)調(diào)用行。
在函數(shù)調(diào)用行,將返回的值存儲(chǔ)在變量musician
中;然后將這個(gè)變量的值打印出來。如果沒有提供中間名,middle_name
將為空字符串,導(dǎo)致if
測(cè)試未通過,進(jìn)而執(zhí)行else
代碼塊:只使用名和姓來生成姓名,并將設(shè)置好格式的姓名返回給函數(shù)調(diào)用行。
在函數(shù)調(diào)用行,將返回的值存儲(chǔ)在變量musician
中;然后將這個(gè)變量的值打印出來。調(diào)用這個(gè)函數(shù)時(shí),如果只想指定名和姓,調(diào)用起來將非常簡單。如果還要指定中間名,就必須確保它是最后一個(gè)實(shí)參,這樣Python才能正確地將位置實(shí)參關(guān)聯(lián)到形參。
這個(gè)修改后的版本適用于只有名和姓的人,也適用于還有中間名的人:
Jimi Hendrix
John Lee Hooker
可選值讓函數(shù)能夠處理各種不同情形的同時(shí),確保函數(shù)調(diào)用盡可能簡單。
本篇文章沒有作業(yè),下一篇我們講解函數(shù)的高級(jí)用法