1. 程式人生 > 實用技巧 >詳解函式和函式的使用

詳解函式和函式的使用

定義函式

下面是一個列印問候語的簡單函式,名為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() 函式時,我們向它傳遞了實參 dogwillie 。與第一次呼叫時一樣,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 中。結果是我們得到了一個名為 Hamsterharry

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知道應該將實參 hamsterharry 分別儲存在形參 animal_typepet_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

可選值讓函式能夠處理各種不同情形的同時,確保函式調用盡可能簡單。

本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,如有問題請及時聯絡我們以作處理

想要獲取更多Python學習資料可以加
QQ:2955637827私聊
或加Q群630390733
大家一起來學習討論吧!