- 函數的基本定義
- 函數參數
- 返回值
- 局部變量和全局變量
- 嵌套函數
- 匿名函數
- 高階函數
- 遞歸
如今你的老闆讓你寫一個監控程序,24小時整年午無休的監控大家公司網站服務器的運行情況,當cpu\memory\disk等指標的使用量超過閾值時即發送報警郵件:java
while True: if cpu利用率 > 90%: #發送郵件提醒 鏈接郵箱服務器 發送郵件 關閉鏈接 if 硬盤使用空間 > 90%: #發送郵件提醒 鏈接郵箱服務器 發送郵件 關閉鏈接 if 內存佔用 > 80%: #發送郵件提醒 鏈接郵箱服務器 發送郵件 關閉鏈接
那麼當你的同事看到這個代碼的時候,就發現了代碼的重複性比較高,每次報警都要重寫一段發郵件的代碼,一個勁的copy and paste根本就不符合高端程序員的氣質,其次若是之後想修改發郵件的代碼,好比加上羣發功能,那麼就須要在全部代碼上都要修改一遍.python
你也看出來了這個問題,你也不想去寫重複代碼,但又不知道怎麼寫,此時你的同時笑着和你說,這個很簡單,只要把重複的代碼提出出來,放在一個公共的地方,起個名字,之後誰想用這個代碼,就經過這個名字就能夠調用了.,以下:linux
def 發送郵件(內容): # 發送郵件提醒 鏈接郵件服務器 發送郵件 關閉鏈接 while True: if cpu利用率 > 90%: 發送郵件('CPU報警') if 硬盤使用空間 > 90%: 發送郵件('硬盤報警') if 內存佔用 > 80%: 發送郵件('內存報警')
函數一詞來源於數學,但編程中的「函數」概念,與數學中的函數是有很大不一樣的,編程中的函數在英文中也有不少不一樣的叫法。在BASIC中叫作subroutine(子過程或子程序),在Pascal中叫作procedure(過程)和function,在C中只有function,在Java裏面叫作method。程序員
定義:函數是指將一組語句的集合經過一個名字(函數名)封裝起來,要想執行這個函數,只需調用其函數名便可編程
- 減小重複代碼
- 使程序變得可擴展
- 使程序變得可維護
def sayhi(): # sayhi函數名 print('hello world') sayhi() # 調用函數
形參變量 只有在調用時猜分配內存單元,在調用結束後,即刻釋放所分配的內存單元.所以,形參只在函數內部有效.函數調用結束返回主調用函數後則不能再使用該形參變量服務器
實參 能夠是常量\變量\表達式\函數等,不管實參是那種類型的量,再進行函數調用時,他們都必須有肯定的值,以便把這些值傳送給形參.所以應預先用賦值,輸入等方法使參數得到肯定值.運維
def cacl(x,y): # 此時的x,y是形參 res = x**y return res c = cacl(2,5) # 此時的2,5是形參 print(c)
看以下代碼:函數
def stu_register(name,age,country,course): print('-----註冊學生信息-----') print('姓名:',name) print('年齡:',age) print('國家:',country) print('課程:',course) stu_register('老王',22,'CHINA','PYTHON') stu_register('小明',18,'CHINA','JAVA') stu_register('康康',19,'CHINA','C++')
運行結果以下:網站
-----註冊學生信息----- 姓名: 老王 年齡: 22 國家: CHINA 課程: PYTHON -----註冊學生信息----- 姓名: 小明 年齡: 18 國家: CHINA 課程: JAVA -----註冊學生信息----- 姓名: 康康 年齡: 19 國家: CHINA 課程: C++
這時候咱們發現了一個問題,咱們發現country這個參數的值都是CHINA,就像咱們在註冊網站的時候,像國家這種信息,你不填寫的話,那麼默認是CHINA,這就是經過默認參數實現的,把country變成默認參數很是簡單.ui
def stu_register(name,age,course,country='CHINA'):
這樣,這個參數在調用時不指定,那麼默認就是CHINA了,若是指定的話,就是你本身指定的值了.舉個例子看看:
# 默認參數--若是用戶指定了參數值,則使用用戶指定的值,不然使用默認參數的值。 def stu_register(name,age,course,country='CHINA',): print('-----註冊學生信息-----') print('姓名:',name) print('年齡:',age) print('國家:',country) print('課程:',course) stu_register('老王',22,'PYTHON') stu_register('小明',18,'JAVA','JAPAN') # 指定了country = JAPAN
那麼咱們看一下運行結果:
-----註冊學生信息----- 姓名: 老王 年齡: 22 國家: CHINA 課程: PYTHON -----註冊學生信息----- 姓名: 小明 年齡: 18 國家: JAPAN 課程: JAVA
那麼,爲何默認參數要放置在參數的最後一個位置呢?讓咱們來看下:
def stu_register(name,age,country='CHINA',course): # 若是寫成這樣的話,那麼會當即報錯,根本就不會運行,由於將實參傳遞給形參時,不知道CHINA是傳給誰的 print('-----註冊學生信息-----') print('姓名:',name) print('年齡:',age) print('國家:',country) print('課程:',course) stu_register('康康',19,'CHINA','C++')
正常狀況下,給函數傳參數要按順序,不想按順序就能夠用關鍵參數,只需指定參數名就能夠了(指定參數名的參數就叫作關鍵參數),注意:關鍵參數必須放在位置參數以後(以位置順序肯定對應關係的參數) 舉個例子看一下:
def stu_register(name,age,course='python',country='CHINA'): # 設置兩個默認參數 print('-----註冊學生信息-----') print('姓名:',name) print('年齡:',age) print('國家:',country) print('課程:',course) stu_register('王佳',course='LIUNX',age = 22)
可是不能夠這樣調用:
stu_register('小明',course='java ',22,country='JAPAN') 也不能夠這樣 stu_register('小明',22,23,country='JAPAN') # 這句話等於將22 23傳遞給了age,可是不能傳遞兩個值
若你的函數中不肯定要傳入多少參數,就可使用非固定函數,舉個例子來講,文章開頭的郵件報警,如今我不想只對一我的發郵件,我想對整個運維部的人發送報警郵件,那麼這個時候使用非固定參數最合適不過了:
def students(name,age,*args): # *args 會把多傳入的參數變成一個元組形式 print(name,age,args) # 打印name,age,args students('小明',22,'PYTHON','CHINA') # 小明 22 ('PYTHON', 'CHINA')
原來,*args所接受的參數是用元組的形式保存的
那麼如今還有一個**kwargs
def stu(name,age,*args,**kwargs): print(name,age,args,kwargs) stu('肖亞飛',22) # 肖亞飛 22 () {} # 讓咱們看看args和kwargs的差異在哪? stu('肖爸爸',23,'python','linux') # 肖爸爸 23 ('python', 'linux') {} stu('肖爸爸',23,'python','linux',addr = 'Henan',music = '蝴蝶') # 肖爸爸 23 ('python', 'linux') {'addr': 'Henan', 'music': '蝴蝶'}
總結:
1.*args必須放在**kwargs以前 2.*args 沒有key值,**kwargs有key值 3.使用*args和**kwargs能夠很是方便的定義函數,同時能夠增強擴展性,以便往後的代碼維護
函數外部的代碼想要獲取函數的執行結果,就能夠在函數裏使用return語句把結果返回
def student(name,age,course='PYTHON',country='CN'): # 設置了course和country兩個默認參數 print('-----註冊學生信息-----') print('姓名:',name) print('年齡:',age) print('國家:',country) print('課程:',course) if age > 20: # 判斷若是age大於20 return False # 返回False else: return True result = student('張三',22) # 函數外部的代碼想要獲取函數的執行結果,就須要使用一個變量來進行接受 if result: # 若是result = True print('恭喜註冊成功!') else: # 若是result = Flase print('年紀太大!')
看一下運行結果:
-----註冊學生信息----- 姓名: 張三 年齡: 22 國家: CN 課程: PYTHON 年紀太大!
注意
- 函數在執行過程當中,只要遇到return語句,就會中止執行並返回結果,so也能夠理解爲return語句表明着函數的結束
- 若是未在函數中指定return,那麼這個函數的返回值爲None
name = 'xiaoyafei' def change_name(name): # 定義一個函數,傳遞Name參數 print('修改前:',name) name = '肖亞飛' print('修改後:',name) change_name(name) print('在外面看修改過了嗎?',name)
輸出結果爲:
修改前: xiaoyafei # 沒修改以前的 修改後: 肖亞飛 # 在函數裏修改過的 在外面看修改過了嗎? xiaoyafei
不用傳name參數,在函數裏面仍是能夠調用外面的變量
name = "xiaoyafei" def change_name(): name = '肖亞飛' print("修改後:",name) change_name() print("在外面看:",name)
運行結果爲:
修改後: 肖亞飛 在外面看: xiaoyafei
那麼,爲啥我明明修改過了,爲啥在外面仍是沒修改呢?讓咱們看看name變量的空間地址:
name = "xiaoyafei" def change_name(): name = '肖亞飛' print("函數內,id:",id(name)) change_name() print("函數外,id:",id(name))
結果以下:
函數內,id: 1388194004688 函數外,id: 1388193784688
要是不放大你的眼睛還真的看不清楚呢,內存空間地址不一樣,因此兩個name是沒有關係的,那麼爲啥一個函數裏能定義兩個甚至更多相同的變量呢?
- 在函數中定義的變量稱爲全局變量,在程序的一開始定義的變量稱爲全局變量
- 全局變量的做用域是整個程序,局部變量的做用是定義該變量的函數
- 當全局變量和局部變量相同時,在定義局部變量的函數內,局部變量起做用;在其餘地方全局變量起做用
做用域,程序設計概念,一般來講,一段代碼中所用到的名字並不老是有效/可用的,而限定這個名字的可用性的代碼範圍就是這個名字的做用與.
name = '肖亞飛' def change_name(): global name # global的做用就是在函數裏聲明全局變量name,意味着最上面的name = '肖亞飛'即便不寫,程序最後面也能夠print name name = 'xiaoyafei' print('函數裏修改:',name) change_name() # 調用函數 print('函數外:',name )
運行結果以下:
函數裏修改: xiaoyafei 函數外: xiaoyafei
name = 'xiaoyafei' def change_name(): name = 'xiaoyafei2' def change_name2(): name = 'xiaoyafei3' # 若是在當前函數擁有name變量,則使用函數內的name變量,若是沒有,則會去上一級去找name,一直找到全局變量 print('第3層打印:',name) # 第3層打印: xiaoyafei3 change_name2() print('第2層的音:',name) # 第2層的音: xiaoyafei2 change_name() print('第一層打印:',name) # 第一層打印: xiaoyafei
匿名函數就是不須要顯示的指定函數名
匿名函數的聲明
res1 = lambda x,y:x+y #聲明一個匿名函數,有x和y兩個參數,操做爲x+y print(res1) # <function <lambda> at 0x0000021D32590D08>
常規函數和匿名函數:
# 常規函數 def cacl(x,y): return x**y res = cacl(2,3) # 若是想要使用函數的返回值,就須要定義一個變量來接收 print(res) # 8 # 匿名函數 res = lambda x,y:x**y print(res(2,3)) # 8
匿名函數的優勢:
- 使用python寫一些腳本時,使用lambda能夠省去自定義函數的過程,讓代碼更加精簡
- 對於一些抽象的,不會被別的地方再重複使用的函數,有時候函數起個名字也是個難題,使用lambda不用考慮這些問題
- 使用lambda在某些時候讓代碼更容易理解
data = list(range(10)) print(data) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 問題:用兩種方法計算這些數的平方 # 常規操做 for index,i in enumerate(data): data[i] = i*i print(data) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # 裝逼操做 res = list(map(lambda x:x*x,data)) print(res) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
那麼在方法2中的map()函數是啥意思?
map()會根據提供的函數對指定序列作映射
第一個參數functrion以參數序列中的每個元素調用function函數,返回包含每次function函數返回值的新列表,能夠不懂,下面就會講,
今天不是看到map()函數,我還真不知道python2和python3中的map()有區別,讓咱們舉個例子看一下map()函數的用法:
在python2中: >>> def cacl(x): # 求一個數的平方 ... return x**2 ... >>> map(cacl,[1,2,3,4,5]) # 在python2中返回的是列表 [1, 4, 9, 16, 25] >>> 在python3中: >>> def cacl(x): ... return x**2 ... >>> map(cacl,[1,2,3,4,5]) # python3中返回的是迭代器對象,何謂迭代器?晚點會在博客中說明 <map object at 0x0000015B373533C8> >>> res = map(cacl,[1,2,3,4,5]) >>> next(res) # 返回迭代器中的下一個值 1 >>> next(res) 4
map()函數在python2和python3中的區別:
- 在python2中返回的是列表
- 在python3中返回的是迭代器對象
map()是python內置的高階函數,它接收一個函數cacl和一個list,並經過函數cacl依次做用於list的每一個元素上,而且獲得一個新的list並返回,因爲list包含的元素能夠是各類類型的,所以,map()不只僅能夠處理只包含數值的list,事實上它也能夠處理包含任意類型的list,只要傳入的函數cacl能夠處理這種數據類型.
變量能夠指向函數,函數的參數能接受變量,那麼一個函數就能夠接收另外一個函數做爲參數,這種函數就被稱爲高階函數.
- 接收一個或多個函數做爲輸入
- return 返回另一個函數
def func2(x,y): return abs,x,y res = func2(-1,10) print(res) # (<built-in function abs>, -1, 10) def cacl(x,y,f): return f(x)+f(y) res = cacl(-4,10,abs) print(res) # 14