內容簡述:
- 1、函式定義
- 2、形參與實參
- 3、關鍵字引數與預設引數
- 4、可變引數
- 5、全域性變數與區域性變數
- 6、內部函式
- 7、閉包
- 8、lambda表示式
- 9、遞迴
1、函式定義
我們可以將一些實現特定功能,重複使用到的「程式碼片段」抽取出來,封裝成一個函式。比如求兩個數和的函式:
def plus(a, b):
"""
計算兩個數的和
:param a: 第一個引數
:param b: 第二個引數
:return: 兩個引數的和
"""
return a + b
if __name__ == '__main__':
print("1 + 2 = %d" % plus(1, 2))
複製程式碼
執行結果如下:
1 + 2 = 3
複製程式碼
從上面的求和函式可以初窺函式定義的一些端倪,接著具體說下規則:
- 函式定義格式:
def 函式名(傳入引數)
- 使用 `return` 返回值,不寫的話預設返回
None
值。 - Python函式的
返回值可以有多個
,本質上利用的元組。 - Python傳遞的
引數有多個
的話,可以用逗號隔開
。 - 一個建議:函式定義時,可在函式的第一行語句中選擇性地使用文件字串編寫函式說明,除了方便閱讀,使用
help(函式名)
也可以拿到這個函式的說明資訊。
2、形參與實參
定義函式時
,函式名後傳入的引數叫「形參」,呼叫函式時
,函式名後傳入的引數叫「實參」。這裡還涉及到一個「傳值與傳址」的問題,傳值就是傳入一個引數的值,而傳址則是傳入一個引數的記憶體地址。兩者的區別是:如果函式是傳值型別的,在函式裡修改了引數的值,外部的變數(實參)是不會改變的,比如下面這樣一段程式碼:
def f(a):
a = 1
b = 0
f(b)
print(b)
複製程式碼
執行結果如下:
0
複製程式碼
儘管我們修改了傳入引數的值,但是實參卻依舊是0,沒有改變,這就是傳參,如果傳遞的是記憶體地址,修改的了話實參也會受影響。但是Python和其他程式語言有點不同(比如C語言裡可以用&引數名傳地址)。
Python不允許開發者選擇採用傳值還是傳址,而是採用「傳物件引用」的方式,如果傳入的引數是一個不可變物件(數字,字串和元組)的引用,就不能修改原始物件的值;如果傳入的引數是一個可變物件(列表,字典)的引用,就能直接修改原始物件的值。
比如下面這樣一串程式碼:
def f(a):
b[0] = 1
b = [0]
f(b)
print(b)
複製程式碼
執行結果如下:
[1]
複製程式碼
3、關鍵字引數與預設引數
「關鍵字引數
」:當函式需要傳入的引數有多個的時候,怕引數混淆傳錯,可以在傳入的時候指定形參的引數名,比如:plus(a = 1, b = 2)。
「預設引數
」:在 定義形參的時候賦予初始值,呼叫的時候就可以不帶引數去呼叫函式,比如:
def plus(a=1, b = 2),呼叫的時候直接plus()或者只傳入一個引數plus(3)都是可以的,還可以配合關鍵字引數指定傳入的是哪個引數。另外,預設引數也稱作「預設引數」。
4、可變引數
有時傳入函式中的 引數數目
可能是 不固定
的,比如,要你計算一堆數字的和,而具體有多少
個數字不知道,這個時候就可以使用可變引數了。只需要在函式定義時在引數前加上*
星號,
就代表這個引數是可變引數(其實是隻是把資料打包成了一個元組)。
另外,如果除了可變引數外還有其他的引數,那麼寫在可變引數後的引數要用關鍵字引數指定,
否則會加入可變引數的範疇。還有一點要注意,如果傳入的引數是列表或者元組,會被再次
打包成元組,如果想解包的話,需要在實參前加*
,程式碼示例如下:
def plus(*a):
result = 0
for b in a:
print(b, end='\t')
if __name__ == '__main__':
a = [1, 2, 3, 4, 5]
plus(a)
print()
plus(*a)
複製程式碼
執行結果如下:
[1, 2, 3, 4, 5]
1 2 3 4 5
複製程式碼
另外,如果想把引數打包成字典的方式,可在函式形參前使用兩個**
標識。
5、全域性變數與區域性變數
全域性變數:定義在最外部,可在函式內部進行訪問,但不能直接修改。
區域性變數:定義在函式內部
,在函式外部無法訪問的引數和變數。
區域性變數無法在外部訪問的原因:
Python在執行函式時,會利用棧(Stack)來儲存資料,執行完函式後,所有資料會被自動刪除。
函式中無法修改全域性變數的原因:
試圖在函式裡修改全域性變數的值時,Python會自動在函式內部新建一個名字一樣的區域性變數代替。如果硬是要修改,可以在函式內部使用
global關鍵字
修飾全域性變數,但是不建議這樣做,會使得程式維護成本的提高。
6、內部函式
所謂的內部函式其實就是「函式巢狀」,在一個函式中巢狀另一個函式,要注意:
內部函式的作用域,只在內部函式的「直接外部函式內」,外部是無法呼叫的沒,外部呼叫內部函式會直接報:函式找不到的錯誤!
內部函式無法直接修改外部函式中的變數,否則會報
UnboundLocalError錯誤
!如果想在內部函式中直接修改,可以把直接外部函式中的變數通過容器型別來存放
,或者使用Python提供的nonlocal關鍵字
修飾。程式碼示例如下:
def fun_x():
x = [10]
y = 10
def fun_y():
x[0] += x[0]
nonlocal y
y *= y
return x[0] * y
return fun_y()
if __name__ == '__main__':
print(fun_x())
複製程式碼
執行結果如下:
2000
複製程式碼
7、閉包
在函式內巢狀了另一個函式,如果「內部函式引用了外部函式的變數」,則可能產生閉包。
Python中形成閉包的三個條件:
- 函式巢狀
- 內部函式引用外部變數
- 外部函式返回內部函式
一個函式閉包的程式碼示例如下:
def outer(a):
b = 1
def inner():
print(a + b)
return inner
if __name__ == '__main__':
test_1 = outer(2)
test_1()
複製程式碼
執行結果如下:
2
複製程式碼
在上面的程式碼中,直接把內部函式當做返回值返回了,b是一個區域性變數,按理來說,生命週期在呼叫完outer()函式後就完結了。但是載上面的程式碼中,呼叫test_1時,b變數的值卻正常輸出了,函式閉包使得函式的「區域性變數資訊」得以儲存。
Python中通過__closure__屬性
儲存閉包中的區域性變數,把上面test_1函式裡的東東
列印出來,程式碼如下:
print(test_1.__closure__)
print(test_1.__closure__[0].cell_contents)
print(test_1.__closure__[1].cell_contents)
複製程式碼
執行結果如下:
(<cell at 0x000001D09ACF85E8: int object at 0x00000000667D6C30>, <cell at 0x000001D09ACF8648: int object at 0x00000000667D6C10>)
2
1
複製程式碼
8、lambda表示式
在Python中可以使用lambda關鍵字
來建立匿名函式,直接返回一個函式物件,而不用去糾結給函式起什麼名字,省去了定義函式的步驟,從而簡化程式碼,一個對比大小簡單的lambda表示式程式碼示例如下:
big = lambda x, y: x > y
print("第一個引數比第二個引數大:%s" % big(1, 2))
複製程式碼
執行結果如下:
第一個引數比第二個引數大:False
複製程式碼
9、遞迴
所謂的遞迴就是「函式呼叫自身」,最簡單的遞迴求和程式碼示例如下:
def sum(n):
if n == 1:
return 1
else:
return n + sum(n - 1)
print("1到100的求和結果是: %d" % sum(100))
複製程式碼
執行結果如下:
1到100的求和結果是: 5050
複製程式碼
另外要注意兩點:
- 遞迴要有結束條件,以避免遞迴的無休止呼叫!
- 遞迴可以簡化程式,但不一定能提高程式的執行效率!
如果本文對你有所幫助,歡迎
留言,點贊,轉發
素質三連,謝謝?~