【Python 1-13】Python手把手教程之——詳解函式和函式的使用
技術標籤:Pythonpythonpython教程python3python2python學習
作者 | 弗拉德
來源 | 弗拉德(公眾號:fulade_me)
定義函式
下面是一個列印問候語的簡單函式,名為greet_user():
def greet_user():
"""顯示簡單的問候語"""
print("Hello!")
greet_user()
這個示例演示了最簡單的函式結構。使用關鍵字def
來告訴Python你要定義一個
函式。在這裡,函式名為greet_user()
,它不需要任何資訊就能完成其工作,因此括號是空的(即便如此,括號也必不可少)。最後,定義以冒號結尾。
緊跟在def greet_user()
:後面的所有縮排行構成了函式體。程式碼行print("Hello!"
)是函式體內的唯一一行程式碼,greet_user()
只做一項工作:列印Hello!。
由於這個函式不需要任何引數,因此呼叫它時只需輸入greet_user()
即可。和預期的一樣,它列印Hello!
。
向函式傳遞資訊
只需稍作修改,就可以讓函式greet_user()
不僅向用戶顯示Hello!
,還將使用者的名字用作引數。
因此,可在函式定義def greet_user()
的括號內新增username
。通過在這裡新增username
,就可讓函式接受你給username
指定的任何值。現在,這個函式要求你呼叫它時給username
greet_user()
時,可將一個名字傳遞給它,如下所示:
def greet_user(username):
"""顯示簡單的問候語"""
print("Hello, " + username.title() + "!")
greet_user('Fulade')
程式碼greet_user('Fulade')
呼叫函式greet_user()
,並向它提供執行print
語句所需的資訊。這個函式接受你傳遞給它的名字,並向這個人發出問候:
Hello Fulade !
同樣,greet_user('sarah')
呼叫函式greet_user()
並向它傳遞sarah
,列印Hello, Sarah!
。
你可以根據需要呼叫函式greet_user()
任意次,呼叫時無論傳入什麼樣的名字,都會生成相應的輸出。
實參和形參
前面定義函式greet_user()
時,要求給變數username
指定一個值。呼叫這個函式並提供這種引數,它將列印相應的問候語。
在函式greet_user()
的定義中,變數username
是一個形參——函式完成其工作所需的一項資訊。在程式碼greet_user('Fulade')
中,值Fulade
是一個實參。
實參是呼叫函式時傳遞給函式的資訊。我們呼叫函式時,將要讓函式使用的資訊放在括號內。在greet_user('Fulade')
中,將實參Fulade
傳遞給了函式greet_user()
,這個值被儲存在形參username
中。
傳遞實參
鑑於函式定義中可能包含多個形參,因此函式呼叫中也可能包含多個實參。向函式傳遞實參的方式很多,可使用位置實參,這要求實參的順序與形參的順序相同;也可使用關鍵字實參,其中每個實參都由變數名和值組成;還可使用列表和字典。下面來依次介紹這些方式。
位置實參
你呼叫函式時,Python必須將函式呼叫中的每個實參都關聯到函式定義中的一個形參。因此,最簡單的關聯方式是基於實參的順序。這種關聯方式被稱為位置實參。為明白其中的工作原理,來看一個顯示寵物資訊的函式。這個函式指出一個寵物屬於哪種動物以及它叫什麼名字,如下所示:
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()
時,需要按順序提供一種動物型別和一個名字。
例如,在前面的函式呼叫中,實參hamster
儲存在形參animal_type
中,而實參harry
儲存在形參pet_name
中。在函式體內,使用了這兩個形參來顯示寵物的資訊。
呼叫函式多次
你可以根據需要呼叫函式任意次。要再描述一個寵物,只需再次呼叫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')
第二次呼叫describe_pet()
函式時,我們向它傳遞了實參dog
和willie
。與第一次呼叫時一樣,Python將實參dog
關聯到形參animal_type
,並將實參willie
關聯到形參pet_name
。
與前面一樣,這個函式完成其任務,但列印的是一條名為Willie
的小狗的資訊。至此,我們有一隻名為Harry
的倉鼠,還有一條名為Willie
的小狗:
I have a hamster.
My hamster's name is Harry.
I have a dog.
My dog's name is Willie.
呼叫函式多次是一種效率極高的工作方式。我們只需在函式中編寫描述寵物的程式碼一次,然後每當需要描述新寵物時,都可呼叫這個函式,並向它提供新寵物的資訊。即便描述寵物的程式碼增加到了10行,你依然只需使用一行呼叫函式的程式碼,就可描述一個新寵物。
在函式中,可根據需要使用任意數量的位置實參,Python將按順序將函式呼叫中的實參關聯到函式定義中相應的形參。
位置實參的順序很重要
使用位置實參來呼叫函式時,如果實參的順序不正確,結果可能出乎意料:
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')
在這個函式呼叫中,我們先指定名字,再指定動物型別。由於實參harry
在前,這個值將儲存到形參animal_type
中;同理,hamster
將儲存到形參pet_name
中。結果是我們得到了一個名為Hamster
的harry
:
I have a harry.
My harry's name is Hamster.
如果結果像上面一樣搞笑,請確認函式呼叫中實參的順序與函式定義中形參的順序一致。
關鍵字實參
關鍵字實參是傳遞給函式的名稱—值對。你直接在實參中將名稱和值關聯起來了,因此向函式傳遞實參時不會混淆。
關鍵字實參讓你無需考慮函式呼叫中的實參順序,還清楚地指出了函式呼叫中各個值的用途。下面來重新編寫pets.py
,在其中使用關鍵字實參來呼叫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')
函式describe_pet()
還是原來那樣,但呼叫這個函式時,我們向Python明確地指出了各個實參對應的形參。看到這個函式呼叫時,Python知道應該將實參hamster
和harry
分別儲存在形參animal_type
和pet_name
中。輸出正確無誤,它指出我們有一隻名為Harry
的倉鼠。
關鍵字實參的順序無關緊要,因為Python知道各個值該儲存到哪個形參中。下面兩個函式呼叫是等效的:
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster')
預設值
編寫函式時,可給每個形參指定預設值。在呼叫函式中給形參提供了實參時,Python將使用指定的實參值;否則,將使用形參的預設值。因此,給形參指定預設值後,可在函式呼叫中省略相應的實參。使用預設值可簡化函式呼叫,還可清楚地指出函式的典型用法。
例如,如果你發現呼叫describe_pet()
時,描述的大都是小狗,就可將形參animal_type
的預設值設定為dog
。這樣,呼叫describe_pet()
來描述小狗時,就可不提供這種資訊:
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')
這裡修改了函式describe_pet()
的定義,在其中給形參animal_type
指定了預設值dog
。這樣,呼叫這個函式時,如果沒有給animal_type
指定值,Python將把這個形參設定為dog
:
I have a dog.
My dog's name is Willie.
請注意,在這個函式的定義中,修改了形參的排列順序。由於給animal_type
指定了預設值,無需通過實參來指定動物型別,因此在函式呼叫中只包含一個實參——寵物的名字。然而,Python依然將這個實參視為位置實參,因此如果函式呼叫中只包含寵物的名字,這個實參將關聯到函式定義中的第一個形參。這就是需要將pet_name放在形參列表開頭的原因所在。
現在,使用這個函式的最簡單的方式是,在函式呼叫中只提供小狗的名字:
describe_pet('willie')
這個函式呼叫的輸出與前一個示例相同。只提供了一個實參——willie
,這個實參將關聯到函式定義中的第一個形參——pet_name
。由於沒有給animal_type
提供實參,因此Python使用其預設值dog
。
如果要描述的動物不是小狗,可使用類似於下面的函式呼叫:
describe_pet(pet_name='harry', animal_type='hamster')
由於顯式地給animal_type
提供了實參,因此Python將忽略這個形參的預設值。
等效的函式呼叫
由於可混合使用位置實參、關鍵字實參和預設值,通常有多種等效的函式呼叫方式。請看下
面的函式describe_pets()
的定義,其中給一個形參提供了預設值:
def describe_pet(pet_name, animal_type='dog'):
基於這種定義,在任何情況下都必須給pet_name
提供實參;指定該實參時可以使用位置方式,也可以使用關鍵字方式。如果要描述的動物不是小狗,還必須在函式呼叫中給animal_type
提供實參;同樣,指定該實參時可以使用位置方式,也可以使用關鍵字方式。
下面對這個函式的所有呼叫都可行:
# 一條名為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')
這些函式呼叫的輸出與前面的示例相同。
避免實參錯誤
等你開始使用函式後,如果遇到實參不匹配錯誤,不要大驚小怪。你提供的實參多於或少於函式完成其工作所需的資訊時,將出現實參不匹配錯誤。
例如,如果呼叫函式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()
Python發現該函式呼叫缺少必要的資訊,而traceback
指出了這一點:
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'
返回值
函式並非總是直接顯示輸出,相反,它可以處理一些資料,並返回一個或一組值。函式返回的值被稱為返回值。在函式中,可使用return
語句將值返回到呼叫函式的程式碼行。返回值讓你能夠將程式的大部分繁重工作移到函式中去完成,從而簡化主程式。
返回簡單值
下面來看一個函式,它接受名和姓並返回姓名:
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)
函式get_formatted_name()
的定義通過形參接受名和姓。它將姓和名合而為一,在它們之間加上一個空格,並將結果儲存在變數full_name
中。然後,將full_name
的值轉換為首字母大寫格式,並將結果返回到函式呼叫行。呼叫返回值的函式時,需要提供一個變數,用於儲存返回的值。
在這裡,將返回值儲存在了變數musician
中。輸出是姓名:
Jimi Hendrix
讓實參變成可選的
有時候,需要讓實參變成可選的,這樣使用函式的人就只需在必要時才提供額外的資訊。可使用預設值來讓實參變成可選的。
例如,假設我們要擴充套件函式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)
只要同時提供名、中間名和姓,這個函式就能正確地執行。它根據這三部分建立一個字串,在適當的地方加上空格,並將結果轉換為首字母大寫格式:
John Lee Hooker
然而,並非所有的人都有中間名,但如果你呼叫這個函式時只提供了名和姓,它將不能正確地執行。為讓中間名變成可選的,可給實參middle_name
指定一個預設值——空字串,並在使用者沒有提供中間名時不使用這個實參。
為讓get_formatted_name()
在沒有提供中間名時依然可行,可給實參middle_name
指定一個預設值——空字串,並將其移到形參列表的末尾:
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)
在這個示例中,姓名是根據三個可能提供的部分建立的。由於人都有名和姓,因此在函式定義中首先列出了這兩個形參。中間名是可選的,因此在函式定義中最後列出該形參,並將其預設值設定為空字串。
在函式體中,我們檢查是否提供了中間名。Python將非空字串解讀為True,因此如果函式呼叫中提供了中間名,if middle_name
將為True
。如果提供了中間名,就將名、中間名和姓合併為姓名,然後將其修改為首字母大寫格式,並返回到函式呼叫行。
在函式呼叫行,將返回的值儲存在變數musician
中;然後將這個變數的值打印出來。如果沒有提供中間名,middle_name
將為空字串,導致if
測試未通過,進而執行else
程式碼塊:只使用名和姓來生成姓名,並將設定好格式的姓名返回給函式呼叫行。
在函式呼叫行,將返回的值儲存在變數musician
中;然後將這個變數的值打印出來。呼叫這個函式時,如果只想指定名和姓,呼叫起來將非常簡單。如果還要指定中間名,就必須確保它是最後一個實參,這樣Python才能正確地將位置實參關聯到形參。
這個修改後的版本適用於只有名和姓的人,也適用於還有中間名的人:
Jimi Hendrix
John Lee Hooker
可選值讓函式能夠處理各種不同情形的同時,確保函式調用盡可能簡單。
本篇文章沒有作業,下一篇我們講解函式的高階用法