Python:函式基礎
目錄
函式基礎
除了可以直接使用的內建函式外,Python還支援自定義函式,即將一段有規律的、可重複使用的程式碼定義成函式,從而達到一次編寫、多次呼叫的目的。
函式的本質就是一段有特定功能、可以重複使用的程式碼,這段程式碼已經被提前編寫好了,並且為其起一個好聽的名字。在後續編寫程式過程中,如果需要同樣的功能,直接通過起好的名字就可以呼叫這段程式碼。
函式的作用
- 結構化程式設計對程式碼的最基本的封裝,一般按照功能組織一段程式碼;
- 封裝的目的為了複用,減少冗餘程式碼;
- 程式碼更加簡潔美觀、可讀易懂
函式的定義
Python函式:能完成一定的功能,由若干語句組成的語句塊、函式名稱、引數列表構成,它是組織程式碼的最小單元。
語法格式
def 函式名(引數1,引數2,引數3,...):
'''註釋'''
函式體
return 返回的值
需要注意的地方:
- 函式名後面必須加冒號;
- 函式名即識別符號,命名規範:小寫字母,多個單詞用
_
間隔; - 如果函式體和def不在同一行,必須縮排,約定4個空格;
- 若沒有
return
,則隱式返回一個None
- 如果函式體body語句只有一行,或者可以簡寫為一行,則可以寫在def的同行。例如:
def myfunc(x,y,z): print(x+y+z)
函式的呼叫
如何呼叫
函式宣告好之後,就可以執行函式,執行函式也稱為呼叫函式,方式為func_name(args)
,例如:
myfunc(1,2,3)
呼叫規則
函式的使用,必須遵循原則:先定義,後呼叫。
若在前面呼叫了,後面再定義,是否會報錯呢?
測試一:
def bar():
print('from bar')
def foo():
print('from foo')
bar()
foo() # 正常
測試二:
def foo():
print('from foo')
bar()
def bar():
print('from bar')
foo() # 也正常
測試三:
def foo():
print('from foo')
bar()
foo()
def bar():
print('from bar')
# 報錯
NameError: name 'bar' is not defined
函式的返回值
函式返回值:
- 沒有返回值:預設返回
None
- 返回一個值:函式結束了且返回一個值
- 返回多個值:多個值之間用逗號隔開,接收的時候可以用一個變數接收(返回元組),也可以用等量的多個變數接收
什麼時候需要有返回值?
呼叫函式,經過一系列的操作,最後要拿到一個明確的結果,則必須要有返回值。
通常有參函式需要有返回值,輸入引數,經過計算,得到一個最終的結果。
什麼時候不需要有返回值?
呼叫函式,僅僅只是執行一系列的操作,最後不需要得到什麼結果,則無需有返回值。
通常無參函式不需要有返回值。
多個返回值
Python的函式支援返回多個值。返回多個值時,預設以tuple的方式返回。例如,下面兩個函式的定義是完全等價的。
def f():
return 1,2
def f():
return (1,2)
丟棄返回值
# 不進行任何賦值,將丟棄所有返回值
f()
# 可以通過索引取得某個或某幾個返回值
a = f()[0]
b = f()[1]
# 使用雙下劃線__或更多下劃線___________
# 丟棄第二個返回值
a, __ = f()
函式呼叫時的*
和**
除了在def
定義函式時,引數中可以使用*
或**
收集引數,在函式呼叫的時候也可以使用*
或**
分別解包元組(列表或其它物件)、字典。一定要注意區分函式定義和函式呼叫時的*
、**
,它們的用法是不通用的。
例如,解包元組:
def f(a,b,c,d):
print(a)
print(b)
print(c)
print(d)
T=(1,2,3,4)
f(*T)
*
除了可以解包元組,還可以解包其它可迭代物件,例如列表。甚至是字典也能解包,只不過*
解包的字典得到的是key組成的引數列表,和value無關:
D=dict(a=11,b=22,c=33,d=44)
f(*D)
# 輸出:
a
b
c
d
而**
解包的字典則是key=value
組成的引數列表。以下是函式呼叫時使用**
進行解包,字典D中的key名稱必須和def中定義的引數名稱相同:
def f(a,b,c,d):
print(a)
print(b)
print(c)
print(d)
D=dict(a=11,b=22,c=33,d=44)
f(**D)
# 輸出:
11
22
33
44
在函式呼叫時,可以混合位置引數、關鍵字引數、*
解包引數、**
解包引數。用法非常的靈活:
def f(a,b,c,d):
print(a)
print(b)
print(c)
print(d)
f(*(1,2),**{'d':4,'c':3})
f(1,*(2,3),**{'d':4})
f(1,c=3,*(2,),**{'d':4})
f(1,*(2,3),d=4)
f(1,*(2,),c=3,**{'d':4})
# 結果如下
1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
上面呼叫函式時的效果都等同於f(1,2,3,4)
。
函式的引數
函式的引數其實也是變數,只不過這些變數是獨屬於函式的本地變數,函式外部無法訪問。在函式呼叫的時候,會將給定的值傳遞給函式的引數,這實際上是變數賦值的過程。例如:
def myfunc(x,y,z):
print(x,y,z)
myfunc(1,2,3)
形參和實參
形參即變數名,實參即變數值,函式呼叫時,將值繫結到變數名上,函式呼叫結束,解除繫結。
形參:定義函式時,小括號中的引數,是用來接收引數用的,在函式內部作為變數使用。
實參:呼叫函式時,小括號中的引數,是用來把資料傳遞到函式內部用的。
引數的傳遞
引數的傳遞可以分為按指標傳參、按位置傳參、按關鍵字key=value
方式傳參。
按指標傳參
Python中變數賦值、引數傳遞都是通過指標拷貝的方式進行的。都只是拷貝了源資料的一個地址,而不會拷貝記憶體中完整的資料物件副本。所以,如果在函式內部修改變數指向的資料物件,會影響函式外部的資料。
例如:
def f(x):
print(x+3)
a = 4
f(a)
# 輸出結果
7
按位置傳參
如果是多個引數,則按從左到右的順序進行引數變數的賦值:
def f(x, y, z):
print(x)
print(y)
print(z)
f(2, 3, 4)
# 輸出結果
2
3
4
呼叫f(2,3,4)的時候,會按照從左向右的位置方式對本地變數x、y、z賦值:x=2,y=3,z=4
。
按關鍵字key=value
方式傳參
Python還支援key=value
的方式設定函式呼叫時的引數,使用key=value
的方式賦值時,順序不重要。這種函式呼叫時的傳值方式稱為關鍵字傳值。
例如:
def f(x, y, z):
print(x)
print(y)
print(z)
f(x=3, y="haha", z=4)
# 輸出
3
haha
4
也可以打亂順序,輸出結果不變:
f(x=3, z=4, y="haha")
還可以將key=value
和位置傳參的方式進行混合:
f(3, "haha", z=4)
但混合按位置傳參方式的時候,位置引數必須在其它傳參方式的前面,不僅此處結合key=value
時如此,後文中位置引數結合其它方式傳參也都如此:位置引數必須在最前面。例如,下面的傳參方式是錯的:
f(z=4, 3, "haha")
引數預設值
在def或lambda宣告函式的時候,可以通過var=default
的方式指定引數的預設值。
例如:
def f(x=3):
print(x)
f(4)
f("haha")
f()
# 輸出結果
4
haha
3
上面的f(4)
和f("haha")
都對函式f()
的本地變數x
進行了賦值。但是最後一個呼叫語句f()
未賦值,而是使用引數的預設值3
。
設定引數預設值時,如果函式有多個引數,則帶預設值引數後面必須放在最後面。例如:
# 正確
def f(x, y, z=4)
def f(x, y=1, z=4)
# 錯誤
def f(x, y=4, z)
只要為引數設定了預設值,那麼呼叫函式的時候,這個引數就是可選的,可有可無的,如果沒有,則採用預設值。
def f(x, y=2, z=4):
print(x)
print(y)
print(z)
# 不採用任何預設值
f(2, 3, 4)
# 採用z的預設值
f(2, 3)
# 採用y的預設值
# 此時z必須按key=value的方式傳值
f(2, z=5)
# y、z都採用預設值
f(2)
# 輸出結果
2
3
4
2
3
4
2
2
5
2
2
4
位置可變引數*
對於任意長度的引數,可以在def
宣告的函式中使用*
將各位置引數收集到一個元組中。
def f(*args):
print(args)
f(1, 2, 3, 4)
# 結果輸出:(1, 2, 3, 4)
上面呼叫f(1, 2, 3, 4)
的時候,將所有引數都收集到了一個名為args
的元組中。
既然是元組,就可以對引數進行迭代遍歷:
def f(*args):
for arg in args:
print(arg)
f(1,2,3,4)
# 輸出結果
1
2
3
4
必須注意,*
是按位置收集引數的。
def f(x, y, *args):
print(x)
print(y)
for arg in args:
print(arg)
f(1, 2, 3, 4)
按照從左向右的傳參規則,首先將1
賦值給x
,將2
賦值給y
,然後將剩餘所有的位置引數收集到args
元組中,所以args=(3,4)
。
如果*
後面還有引數,則呼叫函式的時候,後面的引數必須使用key=value
的方式傳遞,否則會收集到元組中,從而導致引數缺少的問題:
def f(x, *args, y):
print(x)
print(y)
for arg in args:
print(arg)
# 正確
f(1, 3, 4, y=2)
# 錯誤
f(1, 2, 3, 4)
上面呼叫f(1, 3, 4, y=2)
的時候,會按照位置引數對x
賦值為1
,然後將所有位置引數收集到元組args
中,因為y=2
是非位置引數傳值方式,所以args=(3, 4)
。
如果為上面的y設定預設值:
def f(x, *args, y=2)
那麼f(1, 2, 3, 4)
會將(2, 3, 4)
都收集到元組args
中,然後y
採用預設值2
。
關鍵字可變引數**
除了可以使用*
將位置引數收集到元組中,還可以使用**
將key=value
格式的引數收集到字典中。
例如:
def f(x, **args):
print(x)
print(args)
f(1, a=11, b=22, c=33, d=44)
既然是將引數收集到字典中,就可以使用字典類的工具操作這個字典。例如,遍歷字典。
在**
的後面不能出現任何其它型別的引數。例如,下面的都是錯誤的def
定義方式:
def f(x, **args, y)
def f(x, **args, y=3)
def f(x, **args, *t)
只能將位置引數或者*
的收集放在**
的前面。
def f(x, y, **args)
def f(x, *args1, **args2)
keyword-only引數形式
keyword-only的引數傳值方式表示def
中如果使用了*
,那麼在呼叫函式時,它後面的引數必須只能使用關鍵字傳值。其實在前面的內容中已經出現過幾次與之相關的說明。
另外注意,*
才是keyword-only
開關,**
不是,雖然**
也有自己的一些語法限制:任意型別的引數定義都必須在**
之前,包括keyword-only
型別的引數。例如:
def f(a, *b, c):
print(a, b, c)
按照keyword-only
的規則,被*b
收集的位置引數不包括c
,這個c
必須只能使用關鍵字的方式傳值,否則就被當作位置引數被收集到元組b
中。
# 正確
f(1, 2, 3, c=4)
# 錯誤
f(1, 2, 3, 4)
# 錯誤
f(1, c=4, 2, 3)
其中最後一個錯誤和如何def的定義無關,而是函式呼叫時的語法錯誤,前面已經解釋過:位置引數必須放在最前面。
還可以直接使用*
而非*args
的方式,這表示不收集任何引數,但卻要求它後面的引數必須按照關鍵字傳值的方式。
def f(a, *, b, c):
print(a, b, c)
以下是正確和錯誤的呼叫方式示例:
# 正確
f(1, b=2, c=3)
f(1, c=3, b=2)
f(b=2, c=3, a=1)
# 錯誤
f(1, 2, 3)
f(1, 2, c=3)
f(1, b=2, 3)
不過,keyword-only
後面的引數可以使用引數預設值。
def f(a, *, b, c=3)
那麼c
是可選的,但如果給定,則必須按關鍵字方式傳值。