第七篇 python基礎之函數,遞歸,內置函數
第七篇 python基礎之函數,遞歸,內置函數
閱讀目錄
- 一 數學定義的函數與python中的函數
- 二 為何使用函數
- 背景提要
- 三 函數和過程
- 四 函數參數
- 五 局部變量和全局變量
- 六 前向引用之‘函數即變量‘
- 七 嵌套函數和作用域
- 八 遞歸調用
- 九 匿名函數
- 十 函數式編程
- 十一 內置函數
- 十二 本節作業
一 數學定義的函數與python中的函數
初中數學函數定義:一般的,在一個變化過程中,如果有兩個變量x和y,並且對於x的每一個確定的值,y都有唯一確定的值與其對應,那麽我們就把x稱為自變量,把y稱為因變量,y是x的函數。自變量x的取值範圍叫做這個函數的定義域
例如y=2*x
python中函數定義:函數是邏輯結構化和過程化的一種編程方法。
1 python中函數定義方法: 2 3 def test(x): 4 "The function definitions" 5 x+=1 6 return x 7 8 def:定義函數的關鍵字 9 test:函數名 10 ():內可定義形參 11 "":文檔描述(非必要,但是強烈建議為你的函數添加描述信息) 12 x+=1:泛指代碼塊或程序處理邏輯 13 return:定義返回值
調用運行:可以帶參數也可以不帶
函數名()
補充:
1.編程語言中的函數與數學意義的函數是截然不同的倆個概念,編程語言中的函數是通過一個函數名封裝好一串用來完成某一特定功能的邏輯,數學定義的函數就是一個等式,等式在傳入因變量值x不同會得到一個結果y,這一點與編程語言中類似(也是傳入一個參數,得到一個返回值),不同的是數學意義的函數,傳入值相同,得到的結果必然相同且沒有任何變量的修改(不修改狀態),而編程語言中的函數傳入的參數相同返回值可不一定相同且可以修改其他的全局變量值(因為一個函數a的執行可能依賴於另外一個函數b的結果,b可能得到不同結果,那即便是你給a傳入相同的參數,那麽a得到的結果也肯定不同)
2.函數式編程就是:先定義一個數學函數(數學建模),然後按照這個數學模型用編程語言去實現它。至於具體如何實現和這麽做的好處,且看後續的函數式編程。
二 為何使用函數
背景提要
現在老板讓你寫一個監控程序,監控服務器的系統狀況,當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不符合高端程序員的氣質
- 如果日後需要修改發郵件的這段代碼,比如加入群發功能,那你就需要在所有用到這段代碼的地方都修改一遍
你覺得老王說的對,你也不想寫重復代碼,但又不知道怎麽搞,老王好像看出了你的心思,此時他抱起你兒子,笑著說,其實很簡單,只需要把重復的代碼提取出來,放在一個公共的地方,起個名字,以後誰想用這段代碼,就通過這個名字調用就行了,如下
def 發送郵件(內容) #發送郵件提醒 連接郵箱服務器 發送郵件 關閉連接 while True: if cpu利用率 > 90%: 發送郵件(‘CPU報警‘) if 硬盤使用空間 > 90%: 發送郵件(‘硬盤報警‘) if 內存占用 > 80%: 發送郵件(‘內存報警‘)
你看著老王寫的代碼,氣勢恢宏、磅礴大氣,代碼裏透露著一股內斂的傲氣,心想,老王這個人真是不一般,突然對他的背景更感興趣了,問老王,這些花式玩法你都是怎麽知道的? 老王親了一口你兒子,捋了捋不存在的胡子,淡淡的講,“老夫,年少時,師從京西沙河淫魔銀角大王 ”, 你一聽“銀角大王”這幾個字,不由的嬌軀一震,心想,真nb,怪不得代碼寫的這麽6, 這“銀角大王”當年在江湖上可是數得著的響當當的名字,只可惜後期縱欲過度,卒於公元2016年, 真是可惜了,只留下其哥哥孤守當年兄弟倆一起打下來的江山。 此時你看著的老王離開的身影,感覺你兒子跟他越來越像了。。。
總結使用函數的好處:
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
四 函數參數
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 = "Alex" 2 3 def change_name(): 4 name = "Alex2" 5 6 def change_name2(): 7 name = "Alex3" 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=‘alex‘ 3 4 def foo(): 5 name=‘lhf‘ 6 def bar(): 7 print(name) 8 return bar 9 10 func=foo() 11 func() 12 13 14 例二: 15 name=‘alex‘ 16 17 def foo(): 18 name=‘lhf‘ 19 def bar(): 20 name=‘wupeiqi‘ 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. 必須有一個明確的結束條件
2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
3. 遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出)
堆棧掃盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html
尾遞歸優化:http://egon09.blog.51cto.com/9161406/1842475
九 匿名函數
匿名函數就是不需要顯式的指定函數
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
十 函數式編程
峰哥原創面向過程解釋:
函數的參數傳入,是函數吃進去的食物,而函數return的返回值,是函數拉出來的結果,面向過程的思路就是,把程序的執行當做一串首尾相連的函數,一個函數吃,拉出的東西給另外一個函數吃,另外一個函數吃了再繼續拉給下一個函數吃。。。
例如:
用戶登錄流程:前端接收處理用戶請求-》將用戶信息傳給邏輯層,邏輯詞處理用戶信息-》將用戶信息寫入數據庫
驗證用戶登錄流程:數據庫查詢/處理用戶信息-》交給邏輯層,邏輯層處理用戶信息-》用戶信息交給前端,前端顯示用戶信息
函數式編程:
http://egon09.blog.51cto.com/9161406/1842475
11 高階函數
滿足倆個特性任意一個即為高階函數
1.函數的傳入參數是一個函數名
2.函數的返回值是一個函數名
十一 內置函數
內置參數詳解 https://docs.python.org/3/library/functions.html?highlight=built#ascii
十二 本節作業
有以下員工信息表
當然此表你在文件存儲時可以這樣表示
1 1,Alex Li,22,13651054608,IT,2013-04-01
現需要對這個員工信息文件,實現增刪改查操作
- 可進行模糊查詢,語法至少支持下面3種:
- select name,age from staff_table where age > 22
- select * from staff_table where dept = "IT"
- select * from staff_table where enroll_date like "2013"
- 查到的信息,打印後,最後面還要顯示查到的條數
- 可創建新員工紀錄,以phone做唯一鍵,staff_id需自增
- 可刪除指定員工信息紀錄,輸入員工id,即可刪除
- 可修改員工信息,語法如下:
- UPDATE staff_table SET dept="Market" WHERE where dept = "IT"
註意:以上需求,要充分使用函數,請盡你的最大限度來減少重復代碼!
第七篇 python基礎之函數,遞歸,內置函數