【Python 1-13】Python手把手教程之——詳解函數(shù)和函數(shù)的使用

作者 | 弗拉德
來源 | 弗拉德(公眾號(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í)參dogwillie。與第一次調(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è)名為Hamsterharry

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í)參hamsterharry分別存儲(chǔ)在形參animal_typepet_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í)用法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容