Python 函式,參數,傳回值介紹,以及 Python 函式的定義和呼叫
先決條件
閱讀本節的先決條件是已經掌握函式,參數等概念,你可以檢視程式設計教學的函式,參數,傳回值介紹一節來了解他們。
定義 Python 函式
通常情況下,Python 函式定義在模組中,並作為可被外部呼叫的目標,其基本的書寫格式如下。
def <funcname>(<parameterlist>)
<docstring>
<block>
- funcname 部分
funcname
為函式名稱,他需要符合 Python 的識別碼規格,不能使用 Python 關鍵字或保留關鍵字。- parameterlist 部分
parameterlist
是函式的參數表,他定義了函式的所有參數,參數之間使用,
進行分隔。- docstring 部分
docstring
是函式的文件字串,包含了對函式的說明,這並非強製性的要求,一個函式可以沒有文件字串。- block 部分
block
為函式的主體程式碼,他和docstring
都需要使用某種空白字元進行縮排,以表示其歸屬於函式。
下面是一個簡單的 Python 函式hello
,擁有一個名稱為name
的參數。
# 定義一個具有參數 name 的函式 hello
def hello(name):
'''一個超級簡單的函式'''
print(f'你好,{name}')
呼叫 Python 函式
在任何語言中,呼叫函式都是一件輕松的事情,Python 也是如此,以下是呼叫函式的基本格式。
<funcname>(<argumentlist>)
- funcname 部分
funcname
為需要呼叫的函式名稱。- argumentlist 部分
argumentlist
是向函式傳遞的引數表。
在下面的程式碼中,我們呼叫了函式hello
,並傳遞了引數'Kitty'
。
# 呼叫函式 hello
hello('Kitty')
你好,Kitty
Python 程式碼縮排
Python 通過縮排來說明程式碼層級結構,縮排一般通過空格字元或定位字元實作。從行開頭開始計算,擁有相同數量空格字元或定位字元的程式碼被視為同一層次。
不要在 Python 中混合使用空格字元和定位字元進行縮排
雖然,在一個 Python 腳本檔案中,混合使用空格字元和定位字元進行縮排是被允許的,比如,使用兩個空格表示一個層次,使用三個定位字元表示另一個層次。但這並不是可取的行為,因為他可能會導致不必要的錯誤,或讓程式碼的可讀性下降。
Python 函式的參數
Python 函式的參數可以擁有預設值,在定義時使用=
進行設定即可,預設值僅運算一次,因此參數的預設值會指向同一個物件。如果預設值是一個串列,那麽每次呼叫函式時,串列包含的元素可能會不同。
如何限製 Python 函式的參數為位置參數?
為 Python 函式增加特殊符號/
,該符號之前的參數只能作為位置參數使用。
如何限製 Python 函式的參數為關鍵字參數?
為 Python 函式增加*
標記的可變參數(可以僅保留*
自身),該參數之後的參數只能作為關鍵字(命名)參數使用。在一個函式中,使用*
標記的參數只能出現在符號/
之後,否則位置參數將無法被確定。
下面的函式add
,其參數x
,y
擁有預設值,且只能作為位置參數使用,參數z
只能作為關鍵字參數使用。當參數y
使用其預設值時,串列中的元素會隨著呼叫次數的增多而增多。
# 定義加法函式
def add(x=1, y=[], /, *, z):
# 串列 y 新增數值 100
y.append(100)
print(y)
# 計算 x,y,z 含有的數值之和
num = 0
for i in y:
num += i
return x + num + z
# z 只能作為關鍵字參數使用
print(add(z=1))
# x 作為位置參數被使用
print(add(1, z=3))
[100]
102
[100, 100]
204
Python 函式的可變參數
在 Python 函式的參數前新增*
或**
後,參數將成為可變參數。其中符號*
標記的參數是一個元組,他包含了呼叫該函式時,所有未在參數表中的位置參數。符號**
標記的參數是一個字典,他應該位於參數表的最後,包含了呼叫該函式時,所有未在參數表中的關鍵字參數,字典鍵值組的鍵是一個字串,表示了參數的名稱,鍵值組的值為參數對應的值。
如果在參數表中同時使用了*
和**
,則*
後需要具有參數名稱。
如何為 Python 函式傳遞串列,元組,區域,字典中的值?
如果為函式參數準備的值,儲存在 Python 序列或對映物件中,那麽可通過拆解操作來簡化參數的傳遞。具體做法為,使用*
拆解序列物件(包括串列,元組,區域range
),使用**
拆解對映物件(主要為字典)。
下面的函式mlp
,其參數nums
,info
均為可變參數。在呼叫時,我們為mlp
傳遞了多個數值,以進行乘法計算。
# 定義乘法函式
def mlp(*nums, **info):
# 顯示關鍵字參數
print(info)
# 計算 nums 中包含的位置參數
num = 1
for i in nums:
num *= i
return num
# 傳遞了多個數值進行乘法計算
print(mlp(2, 3, 4, 5, tip='這裏是一個小小的提示'))
{'tip': '這裏是一個小小的提示'}
120
Python 函式的傳回值
使用return
陳述式,可以為 Python 函式傳回指定的值,如果未使用return
或return
未給出傳回值,則函式預設傳回None
。
Python 函式的簽章
在預設情況下,Python 函式的簽章僅包含函式名稱,這意味著在同一個命名空間中,之後定義的函式會覆蓋之前定義的同名函式,無論其參數和傳回值如何。
在下面的程式碼中,第二次定義的函式wait
將覆蓋第一次定義的wait
,因此,最後的陳述式wait(3)
呼叫的是第二個wait
函式。
# 該函式將被之後定義的 wait 覆蓋
def wait(seconds):
print(f'等待 {seconds} 秒')
# 呼叫的是最先定義的 wait 函式
wait(3)
# 該函式將覆蓋之前定義的 wait
def wait(hours, minutes=5):
print(f'等待 {hours} 小時 {minutes} 分')
# 呼叫的是最後定義的 wait 函式
wait(3)
等待 3 秒
等待 3 小時 5 分
Python 函式的命名空間
是的,Python 函式擁有自己的命名空間,因此,你在函式內定義的變數,可以與函式外部的變數同名,他們會被區別對待。
Python 函式不能直接指派修改函式外部定義的變數
根據有效範圍的一般性原則,你可以在函式中存取函式外部定義的某個變數,但僅限於讀取,當你嘗試對外部變數寫入時,並不能達到預期效果,原因在於 Python 會將指派陳述式,認定為定義新的變數。
要實作在函式中修改外部變數,你需要使用global
或nonlocal
關鍵字。
命名空間,有效範圍
想要深入了解命名空間和有效範圍,你可以檢視程式設計教學的命名空間,有效範圍介紹一節。
下面的函式show
並沒有修改模組中定義的message
變數,而是定義了自己的message
。
# 在模組中定義變數 message
message = '我是一個模組變數'
# 函式 show 定義了自己的 message
def show():
# 這並不會修改模組定義的 message
message = '我是函式中的變數'
print(message)
# 函式 show_again 使用了模組定義的 message
def show_again():
print(message)
show()
show_again()
我是函式中的變數
我是一個模組變數
Python 巢狀函式
巢狀函式是定義在另一個函式或方法中的函式,其采用的語法和普通函式沒有區別,被定義的巢狀函式的可存取性,一般等同於同一命名空間中的變數。
當然,你可以通過傳回值或其他方式,使巢狀函式在更多位置得以呼叫,但這有違定義巢狀函式的初衷。
函式check
是定義在函式add_list
中的巢狀函式,用於排除元組中大於10
的數值。
# 計算元組中所有小於等於 10 的數值之和
def add_list(*l):
# 巢狀函式 check,檢查數值,大於 10 則傳回 0
def check(i):
return 0 if i > 10 else i
# 呼叫 check,忽略元組中大於 10 的數值
num = 0
for i in l:
num += check(i)
return num
print(add_list(4, 2, 11, 3))
9
Python Lambda 運算式
使用 lambda 運算式,你可以簡便的定義一個匿名函式。與普通的函式不同,lambda 運算式定義的函式,不支援文件字串以及參數或傳回值的批註,並且僅允許單一的運算式,該運算式的運算結果即為函式的傳回值,不需要使用return
陳述式。
在下面的程式碼中,我們使用 lambda 運算式定義了一個完成除法運算的匿名函式。
# 使用 lambda 運算式定義匿名函式
div = lambda a, b: a / b
print(div(9, 3))
3.0
Python 函式註解
書寫 Python 函式的文件字串,可以讓開發人員輕松了解函式的用法,當然這需要開發工具具備相應的程式碼提示功能。
文件字串本質上是書寫在函式中的字串常值,他必須是函式主體的第一行程式碼,可以采用緊密相鄰或加入空白字元的方式進行拼接,比如,
'這是一個非常有用的'"函式"
,但不應包含需要運算的內容或操作,比如,'這是一個非常有用的'+"函式"
,f'他可以對 {count} 個目標進行計算'
。當使用三重引號時,文件字串可以是多行的,字串中的空白字元均會保留,但這僅體現在函式物件的
__doc__
變數中,開發工具應對空白字元進行處理,以向開發人員展現更加友好的資訊。如何為 Python 函式的參數和傳回值新增批註?
與函式的文件字串不同,批註主要用於說明函式參數和傳回值的型別,多個型別可以使用
|
分隔。參數批註的方式為,在參數名稱後書寫:
,然後跟隨參數的型別,比如name: str
。傳回值批註的方式為,在def
陳述式末尾的:
之前書寫->
,然後跟隨傳回值的型別,比如def hi() -> str | None
。所有批註會作為一個字典儲存在函式物件的
__annotations__
變數中,字典鍵值組的鍵是一個字串,其內容為return
(表示傳回值)或參數名稱,鍵值組的值為表示傳回值或參數型別的型別物件。