Python學習之路6?函數,遞歸,內置函數
一python中的函數
函數是邏輯結構化和過程化的一種編程方法。
python中函數定義方法: def test(x): "The function definitions" x+=1 return x def:定義函數的關鍵字 test:函數名 ():內可定義形參 "":文檔描述(非必要,但是強烈建議為你的函數添加描述信息) x+=1:泛指代碼塊或程序處理邏輯 return:定義返回值 調用運行:可以帶參數也可以不帶 函數名()
二 為何使用函數
背景提要
現在老板讓你寫一個監控程序,監控服務器的系統狀況,當cpu\memory\disk等指標的使用量超過閥值時即發郵件報警,你掏空了所有的知識量,寫出了以下代碼
1 while True: 2 if cpu利用率 > 90%: 3 #發送郵件提醒 4 連接郵箱服務器 5 發送郵件 6 關閉連接 7 8 if 硬盤使用空間 > 90%: 9 #發送郵件提醒 10 連接郵箱服務器 11 發送郵件 12 關閉連接 13 14 if 內存占用 > 80%: 15 #發送郵件提醒 16 連接郵箱服務器 17 發送郵件18 關閉連接
上面的代碼實現了功能,你這個重復代碼太多了,每次報警都要重寫一段發郵件的代碼,太low了,這樣幹存在2個問題:
- 代碼重復過多,一個勁的copy and paste不符合高端程序員的氣質
- 如果日後需要修改發郵件的這段代碼,比如加入群發功能,那你就需要在所有用到這段代碼的地方都修改一遍
其實很簡單,只需要把重復的代碼提取出來,放在一個公共的地方,起個名字,以後誰想用這段代碼,就通過這個名字調用就行了,如下
1 def 發送郵件(內容) 2 #發送郵件提醒 3 連接郵箱服務器 4 發送郵件 5 關閉連接 6 7while True: 8 9 if cpu利用率 > 90%: 10 發送郵件(‘CPU報警‘) 11 12 if 硬盤使用空間 > 90%: 13 發送郵件(‘硬盤報警‘) 14 15 if 內存占用 > 80%: 16 發送郵件(‘內存報警‘)
總結使用函數的好處:
1.代碼重用
2.保持一致性,易維護
3.可擴展性
三 函數和過程
過程定義:過程就是簡單特殊沒有返回值的函數
這麽看來我們在討論為何使用函數的的時候引入的函數,都沒有返回值,沒有返回值就是過程,沒錯,但是在python中有比較神奇的事情
1 def test01(): 2 msg=‘hello The little green frog‘ 3 print msg 4 5 def test02(): 6 msg=‘hello WuDaLang‘ 7 print msg 8 return msg 9 10 11 t1=test01() 12 13 t2=test02() 14 15 16 print ‘from test01 return is [%s]‘ %t1 17 print ‘from test02 return is [%s]‘ %t2
總結:當一個函數/過程沒有使用return顯示的定義返回值時,python解釋器會隱式的返回None,
所以在python中即便是過程也可以算作函數。
1 def test01(): 2 pass 3 4 def test02(): 5 return 0 6 7 def test03(): 8 return 0,10,‘hello‘,[‘alex‘,‘lb‘],{‘WuDaLang‘:‘lb‘} 9 10 t1=test01() 11 t2=test02() 12 t3=test03() 13 14 15 print ‘from test01 return is [%s]: ‘ %type(t1),t1 16 print ‘from test02 return is [%s]: ‘ %type(t2),t2 17 print ‘from test03 return is [%s]: ‘ %type(t3),t3
總結:
返回值數=0:返回None
返回值數=1:返回object
返回值數>1:返回tuple
四 函數是第一類對象
在python中所有的名字都沒有儲值功能
函數是第
1 def foo(): 2 print("yyp") 3 f1=foo 4 f1() 5 # 輸出結果: 6 yyp
可以當做參數
1 def foo(): 2 print("tom") 3 4 def func(msg): 5 print(msg) 6 msg() 7 8 func(foo) 9 輸出結果: 10 <function foo at 0x00000000020D3E18> 11 tom
可以當做返回值
1 def foo(): 2 print("tom") 3 4 def func(msg): 5 return msg 6 7 f=func(foo) 8 print(f) 9 f() 10 輸出結果: 11 <function foo at 0x0000000002423E18> 12 tom
可以當做容器類型的一個元素
1 def foo(): 2 print("tom") 3 4 func_dic={ 5 ‘foo‘:foo 6 } 7 輸出結果: 8 tom
一類對象指的是:函數可以被當做數據來處理被引用
五 函數參數
1.形參變量只有在被調用時才分配內存單元,在調用結束時,即刻釋放所分配的內存單元。因此,形參只在函數內部有效。函數調用結束返回主調用函數後則不能再使用該形參變量
2.實參可以是常量、變量、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須有確定的值,以便把這些值傳送給形參。因此應預先用賦值,輸入等辦法使參數獲得確定值
3.位置參數和關鍵字(標準調用:實參與形參位置一一對應;關鍵字調用:位置無需固定)
4.默認參數
5.參數組
六 局部變量和全局變量
在子程序中定義的變量稱為局部變量,在程序的一開始定義的變量稱為全局變量。
全局變量作用域是整個程序,局部變量作用域是定義該變量的子程序。 當全局變量與局部變量同名時: 在定義局部變量的子程序內,局部變量起作用;在其它地方全局變量起作用。1 name=‘lhf‘ 2 3 def change_name(): 4 print(‘我的名字‘,name) 5 6 change_name() 7 8 9 def change_name(): 10 name=‘帥了一筆‘ 11 print(‘我的名字‘,name) 12 13 change_name() 14 print(name) 15 16 17 18 def change_name(): 19 global name 20 name=‘帥了一筆‘ 21 print(‘我的名字‘,name) 22 23 change_name() 24 print(name)
七 前向引用之‘函數即變量‘
1 def action(): 2 print ‘in the action‘ 3 logger() 4 action() 5 報錯NameError: global name ‘logger‘ is not defined 6 7 8 def logger(): 9 print ‘in the logger‘ 10 def action(): 11 print ‘in the action‘ 12 logger() 13 14 action() 15 16 17 def action(): 18 print ‘in the action‘ 19 logger() 20 def logger(): 21 print ‘in the logger‘ 22 23 action()
八 嵌套函數和作用域
看上面的標題的意思是,函數還能套函數?of course
1 name = "yyp" 2 3 def change_name(): 4 name = "yyp2" 5 6 def change_name2(): 7 name = "yyp3" 8 print("第3層打印",name) 9 10 change_name2() #調用內層函數 11 print("第2層打印",name) 12 13 14 change_name() 15 print("最外層打印",name)
此時,在最外層調用change_name2()會出現什麽效果?
沒錯, 出錯了, 為什麽呢?
作用域在定義函數時就已經固定住了,不會隨著調用位置的改變而改變
1 例一: 2 name=‘yyp‘ 3 4 def foo(): 5 name=‘sy‘ 6 def bar(): 7 print(name) 8 return bar 9 10 func=foo() 11 func() 12 13 14 例二: 15 name=‘yyp‘ 16 17 def foo(): 18 name=‘sy‘ 19 def bar(): 20 name=‘tom‘ 21 def tt(): 22 print(name) 23 return tt 24 return bar 25 26 func=foo() 27 func()()
九 遞歸調用
1 def calc(n): 2 print(n) 3 if int(n/2) ==0: 4 return n 5 return calc(int(n/2)) 6 7 calc(10) 8 9 輸出: 10 10 11 5 12 2 13 1
1 #_*_coding:utf-8_*_ 2 __author__ = ‘Linhaifeng‘ 3 import time 4 5 person_list=[‘alex‘,‘wupeiqi‘,‘yuanhao‘,‘linhaifeng‘] 6 def ask_way(person_list): 7 print(‘-‘*60) 8 if len(person_list) == 0: 9 return ‘沒人知道‘ 10 person=person_list.pop(0) 11 if person == ‘linhaifeng‘: 12 return ‘%s說:我知道,老男孩就在沙河匯德商廈,下地鐵就是‘ %person 13 print(‘hi 美男[%s],敢問路在何方‘ %person) 14 print(‘%s回答道:我不知道,但念你慧眼識豬,你等著,我幫你問問%s...‘ %(person,person_list)) 15 time.sleep(3) 16 res=ask_way(person_list) 17 # print(‘%s問的結果是: %res‘ %(person,res)) 18 return res 19 20 21 22 res=ask_way(person_list) 23 24 print(res)遞歸問路
遞歸特性:
1. 必須有一個明確的結束條件
2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
3. 遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出)
鏈接:堆棧掃盲
1 data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] 2 3 4 def binary_search(dataset,find_num): 5 print(dataset) 6 7 if len(dataset) >1: 8 mid = int(len(dataset)/2) 9 if dataset[mid] == find_num: #find it 10 print("找到數字",dataset[mid]) 11 elif dataset[mid] > find_num :# 找的數在mid左面 12 print("\033[31;1m找的數在mid[%s]左面\033[0m" % dataset[mid]) 13 return binary_search(dataset[0:mid], find_num) 14 else:# 找的數在mid右面 15 print("\033[32;1m找的數在mid[%s]右面\033[0m" % dataset[mid]) 16 return binary_search(dataset[mid+1:],find_num) 17 else: 18 if dataset[0] == find_num: #find it 19 print("找到數字啦",dataset[0]) 20 else: 21 print("沒的分了,要找的數字[%s]不在列表裏" % find_num) 22 23 24 binary_search(data,66)二分查找
十 匿名函數
匿名函數就是不需要顯式的指定函數
1 #這段代碼 2 def calc(n): 3 return n**n 4 print(calc(10)) 5 6 #換成匿名函數 7 calc = lambda n:n**n 8 print(calc(10))
匿名函數主要是和其它函數搭配使用的呢,如下
1 l=[3,2,100,999,213,1111,31121,333] 2 print(max(l)) 3 4 dic={‘k1‘:10,‘k2‘:100,‘k3‘:30} 5 6 7 print(max(dic)) 8 print(dic[max(dic,key=lambda k:dic[k])])
1 res = map(lambda x:x**2,[1,5,7,4,8]) 2 for i in res: 3 print(i) 4 5 輸出 6 1 7 25 8 49 9 16 10 64
十一 函數式編程
函數式編程:
http://egon09.blog.51cto.com/9161406/1842475
11 高階函數
滿足倆個特性任意一個即為高階函數
1.函數的傳入參數是一個函數名
2.函數的返回值是一個函數名
1 array=[1,3,4,71,2] 2 3 ret=[] 4 for i in array: 5 ret.append(i**2) 6 print(ret) 7 8 #如果我們有一萬個列表,那麽你只能把上面的邏輯定義成函數 9 def map_test(array): 10 ret=[] 11 for i in array: 12 ret.append(i**2) 13 return ret 14 15 print(map_test(array)) 16 17 #如果我們的需求變了,不是把列表中每個元素都平方,還有加1,減一,那麽可以這樣 18 def add_num(x): 19 return x+1 20 def map_test(func,array): 21 ret=[] 22 for i in array: 23 ret.append(func(i)) 24 return ret 25 26 print(map_test(add_num,array)) 27 #可以使用匿名函數 28 print(map_test(lambda x:x-1,array)) 29 30 31 #上面就是map函數的功能,map得到的結果是可叠代對象 32 print(map(lambda x:x-1,range(5)))map函數
from functools import reduce #合並,得一個合並的結果 array_test=[1,2,3,4,5,6,7] array=range(100) #報錯啊,res沒有指定初始值 def reduce_test(func,array): l=list(array) for i in l: res=func(res,i) return res # print(reduce_test(lambda x,y:x+y,array)) #可以從列表左邊彈出第一個值 def reduce_test(func,array): l=list(array) res=l.pop(0) for i in l: res=func(res,i) return res print(reduce_test(lambda x,y:x+y,array)) #我們應該支持用戶自己傳入初始值 def reduce_test(func,array,init=None): l=list(array) if init is None: res=l.pop(0) else: res=init for i in l: res=func(res,i) return res print(reduce_test(lambda x,y:x+y,array)) print(reduce_test(lambda x,y:x+y,array,50))reduce函數
1 #電影院聚集了一群看電影bb的傻逼,讓我們找出他們 2 movie_people=[‘alex‘,‘wupeiqi‘,‘yuanhao‘,‘sb_alex‘,‘sb_wupeiqi‘,‘sb_yuanhao‘] 3 4 def tell_sb(x): 5 return x.startswith(‘sb‘) 6 7 8 def filter_test(func,array): 9 ret=[] 10 for i in array: 11 if func(i): 12 ret.append(i) 13 return ret 14 15 print(filter_test(tell_sb,movie_people)) 16 17 18 #函數filter,返回可叠代對象 19 print(filter(lambda x:x.startswith(‘sb‘),movie_people))filter函數
1 #當然了,map,filter,reduce,可以處理所有數據類型 2 3 name_dic=[ 4 {‘name‘:‘alex‘,‘age‘:1000}, 5 {‘name‘:‘wupeiqi‘,‘age‘:10000}, 6 {‘name‘:‘yuanhao‘,‘age‘:9000}, 7 {‘name‘:‘linhaifeng‘,‘age‘:18}, 8 ] 9 #利用filter過濾掉千年王八,萬年龜,還有一個九千歲 10 def func(x): 11 age_list=[1000,10000,9000] 12 return x[‘age‘] not in age_list 13 14 15 res=filter(func,name_dic) 16 for i in res: 17 print(i) 18 19 res=filter(lambda x:x[‘age‘] == 18,name_dic) 20 for i in res: 21 print(i) 22 23 24 #reduce用來計算1到100的和 25 from functools import reduce 26 print(reduce(lambda x,y:x+y,range(100),100)) 27 print(reduce(lambda x,y:x+y,range(1,101))) 28 29 #用map來處理字符串列表啊,把列表中所有人都變成sb,比方alex_sb 30 name=[‘alex‘,‘wupeiqi‘,‘yuanhao‘] 31 32 res=map(lambda x:x+‘_sb‘,name) 33 for i in res: 34 print(i)總結
十二 內置函數
1 字典的運算:最小值,最大值,排序 2 salaries={ 3 ‘egon‘:3000, 4 ‘alex‘:100000000, 5 ‘wupeiqi‘:10000, 6 ‘yuanhao‘:2000 7 } 8 9 叠代字典,取得是key,因而比較的是key的最大和最小值 10 >>> max(salaries) 11 ‘yuanhao‘ 12 >>> min(salaries) 13 ‘alex‘ 14 15 可以取values,來比較 16 >>> max(salaries.values()) 17 >>> min(salaries.values()) 18 但通常我們都是想取出,工資最高的那個人名,即比較的是salaries的值,得到的是鍵 19 >>> max(salaries,key=lambda k:salary[k]) 20 ‘alex‘ 21 >>> min(salaries,key=lambda k:salary[k]) 22 ‘yuanhao‘ 23 24 25 26 也可以通過zip的方式實現 27 salaries_and_names=zip(salaries.values(),salaries.keys()) 28 29 先比較值,值相同則比較鍵 30 >>> max(salaries_and_names) 31 (100000000, ‘alex‘) 32 33 34 salaries_and_names是叠代器,因而只能訪問一次 35 >>> min(salaries_and_names) 36 Traceback (most recent call last): 37 File "<stdin>", line 1, in <module> 38 ValueError: min() arg is an empty sequence 39 40 41 42 sorted(iterable,key=None,reverse=False)View Code
Python學習之路6?函數,遞歸,內置函數