函數
為什么要有函數?
補充或改變函數的定義。在還未認識函數之前,一直遵循:面向過程編程。
即:根據業務邏輯從上到下實現功能,其往往用一長段代碼來實現指定功能,開發過程中最常見的操作就是粘貼復制,也就是將之前實現的代碼塊復制到現需功能處,如下:
while True:if cpu利用率 > 90%:#發送郵件提醒 連接郵箱服務器發送郵件關閉連接if 硬盤使用空間 > 90%:#發送郵件提醒 連接郵箱服務器發送郵件關閉連接if 內存占用 > 80%:#發送郵件提醒 連接郵箱服務器發送郵件關閉連接
請補充函數fun。因此我們需要一種重用性和可讀性都更強的方式來解決上述冗長的代碼問題,所以我們接觸到了函數式變成。
?
全部相加的函數?例如上面的代碼可以優化成這樣?
def 發送郵件(內容)#發送郵件提醒 連接郵箱服務器發送郵件關閉連接while True:if cpu利用率 > 90%:發送郵件('CPU報警')if 硬盤使用空間 > 90%:發送郵件('硬盤報警')if 內存占用 > 80%:發送郵件('內存報警')
參照這種情況下照抄一個 武sir 視頻中出現的報警器代碼
https://www.cnblogs.com/evenyao/p/9170493.html
?
函數還能干什么??
先接觸函數,后面我們會進一步對函數進行整合、分類和封裝
- 函數式:將某功能代碼封裝到函數中,日后便無需重復編寫,僅調用函數即可
- 面向對象:對函數進行分類和封裝,讓開發“更快更好更強...”
?
一、定義函數和結構
?通常來說,函數的定義主要是如下結構:
def 函數名(參數):...函數體...return 返回值
- def:表示函數的關鍵字
- 函數名:函數的名稱,日后根據函數名調用函數
- 函數注釋:解釋這段函數
- 函數體:函數中進行一系列的邏輯計算,如:發送郵件、計算出 [11,22,38,888,2]中的最大數等...
- 參數:為函數體提供數據
- 返回值:當函數執行完畢后,可以給調用者返回數據。
?
函數執行的順序
?
?
二、參數
1.普通參數
嚴格按照順序,將實際參數賦值給形式參數
def send(a,b):print("顯示測試",a,b)return Truesend('root','even') #顯示測試 root even
?
?
2.默認參數
定義函數時,必須將默認參數放置在參數列表的最后,例如 def function(a,b = '指定值'):
def send(a,b = '指定值'):print("顯示測試",a,b)return Truesend('root','even') send('root2') #顯示測試 root even #顯示測試 root2 指定值
?
3.指定參數
將實際參數賦值給指定形式參數
def send(a,b):print("顯示測試",a,b)return Truesend('root','even') send(b = 'root',a = 'even') #顯示測試 root even #顯示測試 even root
?
4.動態參數:*args 和 **kwargs
*arges:默認將傳入的參數,全部放置在元組中,如?f1(*[11,22,33])??
def f(*args):print(args,type(args))n1 =[11,22,33,"root"] n2 = "even"f(n1) f(*n1) #將每一個元素轉換到元組里面 f(n2) f(*n2)#([11, 22, 33, 'root'],) <class 'tuple'> #(11, 22, 33, 'root') <class 'tuple'> #('even',) <class 'tuple'> #('e', 'v', 'e', 'n') <class 'tuple'> #相當于做了一次內部for循環
?
**kwargs:默認將傳入的參數,全部放置在字典中,如?f1(**{"k1":"v1","k2":"v2"})
def f(**kwargs):print(kwargs,type(kwargs))f(n1 ="alex") f(n1 ="alex",n2 = 18) dic = {'k1':'v1','k2':'v2'} f(kk =dic)#{'n1': 'alex'} <class 'dict'> #{'n1': 'alex', 'n2': 18} <class 'dict'> #{'kk': {'k1': 'v1', 'k2': 'v2'}} <class 'dict'>
?
當實際參數為?f(**dic) 則直接傳字典。相當于直接把字典賦值到函數中
def f(**kwargs):print(kwargs,type(kwargs))dic = {'k1':'v1','k2':'v2'} f(**dic) #{'k1': 'v1', 'k2': 'v2'} <class 'dict'>
?
5.萬能參數?(*args,**kwargs)?
必須按照順序,名字可以自己命名,但一般使用 *args和 **kwargs
def f1(*args,**kwargs):print(args)print(kwargs) f1(11,22,33,44,k1 = "v1",k2 = "v2") #(11, 22, 33, 44) #{'k1': 'v1', 'k2': 'v2'}
?
三、全局變量與局部變量
在下列情況中,name = 'even' 是屬于 def f1() 中的局部變量,因此def f2()無法使用它
要想 def f2()也能調用到該變量,它必須提到最外層,使其變成全局變量
def f1():name = 'even'print(name)def f2():print(name) #NameError: name 'name' is not defined #even
?
變換之后得到
name = "even"def f1():age = 18print(age,name)def f2():age = 19print(age,name)f1() f2() #18 even #19 even
?
如果一個函數本身有自己的局部變量,優先使用自己的局部變量,如果自己沒有,再去調用全局變量(有點網絡基礎中 DNS查詢順序的意思)
下面示例中由于def f1()中含有局部變量 name = "root",優先使用自己的局部變量,因此輸出結果如下
name = "even"def f1():age = 18name = "root"print(age,name)def f2():age = 19print(age,name)f1() f2() #18 root #19 even
?
***?對全局變量進行【重新賦值】,需要global + 變量名
*** 對于特殊的:列表、字典,讀的過程中,本身可修改,但不可重新賦值
例如:
下面示例中全局變量本來是 name = "even",在def f1()中申明了global name,并在下面對name進行了重新賦值,name = "root"
因此def f2()調用name的時候,也使用name = "root"
name = "even"def f1():age = 18global name #表示name是全局變量name = "root"print(age,name)def f2():age = 19print(age,name)f1() f2()#18 root #19 root
?
但對于特殊的:如列表、字典
因為在調用的過程中,本身可修改,如下
name = [11,22,33,44]def f1():age = 18print(name)name.append(999)print(age,name)def f2():age = 19print(age,name)f1() f2()#[11, 22, 33, 44] #18 [11, 22, 33, 44, 999] #19 [11, 22, 33, 44, 999]
?
***關于全局變量的潛規則:
? ?對于所有的全局變量,建議全部使用大寫來命名
? ?這樣當看到一個大寫的變量名的時候,就可以立即知道這是某個地方的一個全局變量
? ?如:
NAME = "even"def f1():age = 18print(age,NAME)def f2():age = 19print(age,NAME)f1() f2()
?
四、著手使用函數
之前寫過一個用戶注冊并進行登陸驗證的一個程序,但是面向過程編寫的,現在熟悉完了函數之后,我們可以讓這個程序變得更好一些
1 #!/user/bin/env python 2 # -*- coding:utf8 -*- 3 4 5 def main(): 6 print("====== 歡迎登錄系統 ======") 7 choice = input('1:登錄;2:注冊') 8 9 if choice == '1': 10 print("=== 您已進入登錄視圖 ===") 11 user = input("請輸入用戶名>>>") 12 pwd = input("請輸入密碼>>>") 13 l = login(user,pwd) #定義l調用函數def login() 14 if l: 15 print("登錄成功") 16 else: 17 print("登錄失敗") 18 19 elif choice == '2': 20 print("=== 您已進入注冊視圖 ===") 21 user = input("請輸入要注冊的用戶名>>>") 22 pwd = input("請輸入要注冊的密碼>>>") 23 is_exist = user_exist(user) #定義is_exist調用判斷用戶名是否存在的函數def user_exist() 24 if is_exist: #如果True 25 print("用戶已經存在,無法注冊") 26 else: #否則開啟注冊選項 27 result = register(user,pwd) #定義result調用注冊函數def register() 28 if result: 29 print("注冊成功") 30 else: 31 print("注冊失敗") 32 33 else: 34 print("輸入有誤請重新輸入") 35 36 37 def register(username,password): 38 """ 39 用于用戶注冊 40 :param username:用戶名 41 :param password:密碼 42 :return: 43 """ 44 with open('db', "a", encoding="utf-8") as f: 45 temp = "\n" + username + "|" + password 46 f.write(temp) 47 f.close() 48 return True 49 50 51 def login(username,password): 52 """ 53 用于用戶登錄 54 :param username:用戶輸入的用戶名 55 :param password:用戶輸入的密碼 56 :return: True表示登錄成功,False表示登錄失敗 57 """ 58 with open('db','r',encoding='utf-8') as f: 59 for line in f: #一行一行的進行讀取 60 new_line = line.strip().split("|") #移除換行符,并以|符號進行分割 61 if new_line[0] == username and new_line[1] == password: 62 return True 63 return False 64 65 66 def user_exist(username): 67 """ 68 用來檢查用戶名是否存在 69 :param username:用戶名 70 :return:True表示用戶名已經存在,False表示不存在 71 """ 72 with open('db', "r", encoding="utf-8") as f: 73 for line in f: 74 line = line.strip() 75 new_line = line.split("|") 76 if username == new_line[0]: 77 return True 78 return False 79 80 81 main()
?
?
其他補充:
三元(目)運算
三元運算(三目運算),是對簡單的條件語句的縮寫。
?
# 書寫格式 result = 值1 if 條件 else 值2# 如果條件成立,那么將 “值1” 賦值給result變量,否則,將“值2”賦值給result變量
#if,else形式if 1 == 1:name = "even" else:name = "root"#三元運算形式 name = "even" if 1 == 1 else "root"
?
lambda表達式
lambda表達式,是對于簡單函數的一種表示方式。
# ###################### 普通函數 ###################### # 定義函數(普通方式) def func(arg):return arg + 1# 執行函數 result = func(123)# ###################### lambda ####################### 定義函數(lambda表達式) my_lambda = lambda arg : arg + 1# 執行函數 result = my_lambda(123)
?
回顧 format 格式化
s1 = 'i am {0}, age{1}'.format("even",18) print(s1)s2 = 'i am {0}, age{1}'.format(*["even",18]) print(s2) #i am even,age 18. #i am even,age 18.
傳值流程分析
?
?同理于 **kwargs 但是必須指定變量,相當于直接傳字典
s = 'i am {name}, age{age}'.format(name = 'even',age = 18) print(s) #i am even, age 18
dic = {'name':'even','age':18} s = 'i am {name}, age{age}'.format(**dic) print(s) #i am even, age 18
?
?如果還是指定 0,1就會報錯
dic = {0:'even',1:18} s = 'i am {0}, age{1}'.format(**dic) print(s)
?
函數的補充1
有這樣一段代碼? 思考一下最后的分返回值是哪一個? 輸出的結果是什么?
程序執行的過程究竟是怎么樣的?
def f1(a1, a2):return a1 + a2def f1(a1, a2):return a1 * a2ret = f1(8,8) print(ret)
?
經過反復推敲 我們覺得過程其實是這樣的
?
執行到第一句 def 的時候在內存中創建區域 a1 + a2
讓 f1函數指向這個 a1 + a2
但是后來又創建了一個 a1 * a2,就相當于 f1由原來指向的 a1 + a2變成指向 a1 * a2
這個時候 a1 + a2就成為了 "垃圾 "?
好在? Python有一套垃圾清理機制,所以直接就只執行下面這一個函數 a1 * a2
?
我們還通過了pycharm的 debug功能驗證了這個執行順序的真實性
?
函數的補充2
Python函數傳遞參數的時候是是怎樣進行操作的?
def f1(a1):a1.append(999)li = [11,22,33,44] f1(li)print(li)
這里有兩張分析圖,可以用來分析參數傳遞的過程中,到底是引用?還是傳遞另一份值?
?
我們查看一下這個 def 的輸出結果是
#[11, 22, 33, 44, 999]
?
最后我們得出結論Python函數傳遞參數的時候是傳遞的引用,而不是另外一份值,所以傳遞的過程應該是這樣一個圖
所以 li最后輸出為 [11,22,33,44,999]
?
?
那么這一個輸出結果是什么
li = [11,22,33,44]def f1(arg):arg.append(55)li = f1(li) print(li)
#None
?
單看 f1(li)本來已經是 [11,22,33,44,55],但函數的 return默認為 None
又一次重新賦值,所以結果為None....
?