1. 程式人生 > 其它 >【Python 1-13】Python手把手教程之——詳解函式和函式的使用

【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()函式時,我們向它傳遞了實參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

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

本篇文章沒有作業,下一篇我們講解函式的高階用法


公眾號