1. 程式人生 > >決戰Python之巔(十)

決戰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返回另一個函式