決戰Python之巔(十)
前言
昨天將函式前半部分看完了,這裡稍微做一下總結。
知識回顧
函式定義
函式的定義很簡單,如下:
函式是指將一組語句的集合通過一個名字(函式名)封裝起來,要想執行這個函式,只需要呼叫這個函式名即可
注意:呼叫函式名之後,要在函式名後面加一對(),函式才會執行,否則將返回函式的記憶體地址。
在程式碼中定義一個函式:
def calc(x,y):
res = x**y
return res
那為什麼要使用函式呢?
在學習函式之前,假設需要你寫一段程式碼,7*24h監控公司伺服器,當CPU/MEMORY/DISK超過閾值時報警併發送郵件,那你可能會這麼寫:
乍一看這段程式碼能夠將功能實現,但你會發現,這中間有很多重複的程式碼,而且萬一哪天想換個報警的方式,還得換三次。這時候你學到了函式,那你可能會這麼寫:
def send_mail():
#傳送郵件提醒
連線郵箱伺服器
傳送郵件
關閉連線
while True:
if cpu利用率>90%
send_mail()
if 硬碟使用空間>90%:
send_mail()
if 記憶體佔用>90%:
send_mail()
相比較一下,函式的特性變可得出:
- 減少重複的程式碼
- 是程式碼變得可拓展,易維護
函式的引數
這時候又有一個新的需求,就是每次呼叫這個發郵件函式的時候,我可能要發給不一樣的人來維護,比如CPU佔用率的報警我要發給Eric,硬碟使用率要發給Rain,記憶體使用率要發給Kris,這可怎麼辦呢?
之前我們寫的函式裡面只能發給特定的人的郵箱…唔,想一想,我們之前好像已經學過變數,那我們如果將郵箱賬號換成變數,然後每次呼叫函式的時候給變數賦我們需要的值便可。如我們之前定義過的calc():
def calc(x,y):
res = x**y
return res
這裡定義時括號裡的x,y便是形參變數,形參變數只有在被呼叫時才會被分配記憶體單元,在呼叫結束後就會被立刻釋放記憶體單元。因此,形參只在函式內部有效。函式呼叫結束後就不能再使用該函式的形參變數。
而當我們在呼叫這個calc()函式時,是一定要給兩個引數的,即:
calc(3,4)
a=5
b=6
calc(a,b)
這裡使用的3,4或者a,b,稱為實參變數。實參可以是常亮、變數、表示式、函式等,無論是哪種型別,在進行函式呼叫時,必須有明確的值,以便來傳送給形參。
位置引數
在執行上面的程式碼時可以發現,a的值總是傳給了x,b的值總是傳給了y,如果我們給a,b換個位置,執行:
c = calc(b,a)
就會得到另一個結果,並且是b**a,這就意味著這一次b的值給了x,而a的值給了y,這樣以位置順序確定對應關係的引數就叫位置引數。
關鍵引數
如果有一天你不想按順序傳引數了,你可以使用關鍵字引數,又稱關鍵引數。你只需要做的只是指定引數名,就是告訴函式你的這個值是給誰的。
def stu_info(name,age,country,course)
print('姓名:',name)
print('年齡:',age)
print('國籍:',country)
print('課程:',course)
# 關鍵引數
stu_info('山炮',course='Python',age=22,country='CN')
#但絕不可以這樣
stu_info('山炮',course='Python',22,country='CN')
#這樣也不行,同一個引數傳兩個值
stu_info('山炮',25,age=22,country='CN')
注意:關鍵引數要放在位置引數後面,切記。
預設引數
當我們在呼叫上面的函式錄入學生資訊時,我們發現由於學生大部分都是中國學生,所以國籍country這個形參我們傳的大部分都是CN,為了簡便,我們可以將contry變成預設引數,即這個引數在呼叫時不指定,那麼就是你定義的預設值,如果指定了,就用你指定的值。這時,我們就可以這麼定義引數:
def stu_info(name,age,course,country = 'CN')
print('姓名:',name)
print('年齡:',age)
print('課程:',course)
print('國籍:',country)
#呼叫函式
stu_info('山炮',22,'Python') # 這裡使用預設國籍CN
stu_info('鬆島菜菜子',22,'TV','JP') # 這裡使用指定的值
你可能注意到了,在把country變成預設引數後,同時把它移動到了最後面,可以思考下為什麼?
很簡單…
不放在最後會產生歧義。
還是上面的例子…
# 假設我這麼定義,注意這是錯誤的
def stu_info(name,age,country = 'CN',course)
print('姓名:',name)
print('年齡:',age)
print('課程:',course)
print('國籍:',country)
# country使用預設值,course = 'Python'
stu_info('山炮二號',25,'Python')
執行後會報錯,前兩個沒什麼問題,到了第三個這裡,'Python’原本你是想傳給course,但是由於位置的關係,它會傳給country,而最後一個course就沒有值傳給它,它又不是個預設引數,所以會報錯。…其實根本就不能執行,因為定義函式的時候就錯了- -
非固定引數
還是回到最開始發郵件的那個函式,我們已經可以做到給不同的人發郵件,只要定義的時候多加一個引數user:
def send_mail(user):
#傳送郵件提醒
連線郵箱伺服器
傳送郵件給user
關閉連線
現在你的公司擴招了,負責CPU處理的人變多了,這時候你想給他們都發郵件,那你的做法可能是:
for i in users_list:
send_mail(i)
迴圈給他們每個人發郵件…那如果我現在要求只給一部分的人發郵件,不要全部,你可能會想到去改users_list,假如這個list裡有成千上萬人,就是很多很多人,你要給其中一部分發郵件,那修改list的方法似乎有點難以實現…當你不確定你的使用者個數時,你需要定義非固定引數:
def send_mail(*args):
#傳送郵件提醒
連線郵箱伺服器
傳送郵件給user
關閉連線
…一般我們在定義非固定引數時,都用 *args ,當然不用args也可以,但主要是為了規範。
這裡定義的引數就是非固定引數,當你呼叫引數時:
#呼叫引數
send_mail('Alex','Rain','Eric','Kris')
*args
會將你傳給它的值變為一個元組形式,即('Alex','Rain','Eric','Kris')
,然後會將元組中的每個元素依次傳出,相當於for迴圈了這個元組,但是不需要另外寫程式碼,只要定義時注意將引數定義成非固定引數即可。
這樣就實現了可傳入不確定個數的引數。
非固定關鍵字引數
和上面的非固定引數差不多,只不過這裡定義時是使用兩個*,即**kwargs
,這裡傳入值時,需要使用關鍵字引數傳入的形式:
def send_mail(**kwargs):
#傳送郵件提醒
連線郵箱伺服器
傳送郵件給user
關閉連線
#呼叫引數
send_mail(name='Alex',age=22,sex='male')
**kwargs
會將傳給它的引數轉成dict,則上面程式碼呼叫函式的效果就是返回{‘name’:‘Alex’,‘age’:22,‘sex’:‘male’}。
函式的返回值
這裡來處理一個歷史遺留問題,在上面我們定義calc函式時,你會發現函式的最後有一句:return res
。如果函式外部的程式碼想要獲得函式執行的結果,就需要通過return來返回結果。
注意:
- 函式執行過程中只要遇到return語句,就會停止執行返回測試結果。可以理解為第一句return即為函式的結束。
def func(): # 可以思考下執行這個函式會列印2嘛?
print('1')
return True
print('2')
return False
- 如果函式中未指定return的內容,變會返回None。
區域性變數
我們知道了函式定義時可以定義各種變數,但這些變數都有一個限制,就是他們只能在這個函式中使用,在其他函式中想要呼叫是不行的。即區域性變數的作用域是定義該變數的函式。
作用域(scope),程式設計概念,通常來說,一段程式程式碼中所用到的名字並不總是有效/可用的,而限定這個名字的可用性的程式碼範圍就是這個名字的作用域。
而與之相對的則是全域性變數。
全域性變數
在程式一開始定義的變數稱為全域性變數。全域性變數的作用域是整個程式,你在程式重定義的函式也可以呼叫全域性變數。
全域性變數vs區域性變數
- 全域性變數的作用域是整個框即整個程式,而區域性變數的作用域僅限於定義該變數的函式的作用域。
- 當全域性變數與區域性變數重名時,在定義該區域性變數的函式中,區域性變數起作用,其他地方全域性變數起作用。
巢狀函式
巢狀函式就是在一個函式中再定義一個函式:
a = 3
b = 4
def fun1():
print(a)
def func2():
print(b)
#func1 裡呼叫func2
func2()
#呼叫func1()
func1()
這個呼叫的結果就是:3,4
再看這段程式碼:
請問最後輸出的是什麼?
牢記區域性變數的作用域。
匿名函式
匿名函式就是不需要指定顯示的函式名
匿名函式最多支援我們之前學過的三元函式。
主要用處是與其他的函式搭配使用。
高階函式
變數可以指向函式,函式的引數可以接受變數,那麼一個函式就可以接受另一個函式作為引數,這種函式就叫高階函式。
只需要滿足以下任一條件,即為高階函式:
- 接受一個或者多個函式作為輸入
- return返回另一個函式