1、函數python
# 第五章 函數 ## 函數簡介(function) - 函數也是一個對象 - 對象是內存中專門用來存儲數據的一塊區域 - 函數能夠用來保存一些可執行的代碼,而且能夠在須要時,對這些語句進行屢次的調用 - 建立函數: def 函數名([形參1,形參2,...形參n]) : 代碼塊 - 函數名必需要符號標識符的規範 (能夠包含字母、數字、下劃線、可是不能以數字開頭) - 函數中保存的代碼不會當即執行,須要調用函數代碼纔會執行 - 調用函數: 函數對象() - 定義函數通常都是要實現某種功能的 ## 函數的參數 - 在定義函數時,能夠在函數名後的()中定義數量不等的形參, 多個形參之間使用,隔開 - 形參(形式參數),定義形參就至關於在函數內部聲明瞭變量,可是並不賦值 - 實參(實際參數) - 若是函數定義時,指定了形參,那麼在調用函數時也必須傳遞實參, 實參將會賦值給對應的形參,簡單來講,有幾個形參就得傳幾個實參 - 練習1: 定義一個函數,能夠用來求任意三個數的乘積 - 練習2: 定義一個函數,能夠根據不一樣的用戶名顯示不一樣的歡迎信息 ## 函數式編程 - 在Python中,函數是一等對象 - 一等對象通常都會具備以下特色: ① 對象是在運行時建立的 ② 能賦值給變量或做爲數據結構中的元素 ③ 能做爲參數傳遞 ④ 能做爲返回值返回 - 高階函數 - 高階函數至少要符合如下兩個特色中的一個 ① 接收一個或多個函數做爲參數 ② 將函數做爲返回值返回 - 裝飾器
2、函數和參數編程
# 好比有以下三行代碼,這三行代碼是一個完整的功能 # print('Hello') # print('你好') # print('再見') # 定義一個函數 def fn() : print('這是個人第一個函數!') print('hello') print('今每天氣真不錯!') # 打印fn # print(fn) <function fn at 0x03D2B618> # print(type(fn)) <class 'function'> # fn是函數對象 fn()調用函數 # print是函數對象 print()調用函數 # fn() # 定義一個函數,能夠用來求任意兩個數的和 # def sum() : # a = 123 # b = 456 # print(a + b) # sum() # 定義函數時指定形參 def fn2(a , b) : # print('a =',a) # print('b =',b) print(a,"+",b,"=",a + b) # 調用函數時,來傳遞實參 fn2(10,20) fn2(123,456) # 求任意三個數的乘積 def mul(a,b,c): print(a*b*c) # 根據不一樣的用戶名顯示不一樣的歡迎信息 def welcome(username): print('歡迎',username,'光臨') # mul(1,2,3) # welcome('孫悟空') # 定義一個函數 # 定義形參時,能夠爲形參指定默認值 # 指定了默認值之後,若是用戶傳遞了參數則默認值沒有任何做用 # 若是用戶沒有傳遞,則默認值就會生效 def fn(a = 5 , b = 10 , c = 20): print('a =',a) print('b =',b) print('c =',c) # fn(1 , 2 , 3) # fn(1 , 2) # fn() # 實參的傳遞方式 # 位置參數 # 位置參數就是將對應位置的實參複製給對應位置的形參 # 第一個實參賦值給第一個形參,第二個實參賦值給第二個形參 。。。 # fn(1 , 2 , 3) # 關鍵字參數 # 關鍵字參數,能夠不按照形參定義的順序去傳遞,而直接根據參數名去傳遞參數 # fn(b=1 , c=2 , a=3) # print('hello' , end='') # 位置參數和關鍵字參數能夠混合使用 # 混合使用關鍵字和位置參數時,必須將位置參數寫到前面 # fn(1,c=30) def fn2(a): print('a =',a) # 函數在調用時,解析器不會檢查實參的類型 # 實參能夠傳遞任意類型的對象 b = 123 b = True b = 'hello' b = None b = [1,2,3] # fn2(b) fn2(fn) def fn3(a , b): print(a+b) # fn3(123,"456") def fn4(a): # 在函數中對形參進行從新賦值,不會影響其餘的變量 # a = 20 # a是一個列表,嘗試修改列表中的元素 # 若是形參執行的是一個對象,當咱們經過形參去修改對象時 # 會影響到全部指向該對象的變量 a[0] = 30 print('a =',a,id(a)) c = 10 c = [1,2,3] # fn4(c) # fn4(c.copy()) # fn4(c[:]) # print('c =',c,id(c)) # 不定長的參數 # 定義一個函數,能夠求任意個數字的和 def sum(*nums): # 定義一個變量,來保存結果 result = 0 # 遍歷元組,並將元組中的數進行累加 for n in nums : result += n print(result) # sum(123,456,789,10,20,30,40) # 在定義函數時,能夠在形參前邊加上一個*,這樣這個形參將會獲取到全部的實參 # 它將會將全部的實參保存到一個元組中 # a,b,*c = (1,2,3,4,5,6) # *a會接受全部的位置實參,而且會將這些實參統一保存到一個元組中(裝包) def fn(*a): print("a =",a,type(a)) # fn(1,2,3,4,5) # 帶星號的形參只能有一個 # 帶星號的參數,能夠和其餘參數配合使用 # 第一個參數給a,第二個參數給b,剩下的都保存到c的元組中 # def fn2(a,b,*c): # print('a =',a) # print('b =',b) # print('c =',c) # 可變參數不是必須寫在最後,可是注意,帶*的參數後的全部參數,必須以關鍵字參數的形式傳遞 # 第一個參數給a,剩下的位置參數給b的元組,c必須使用關鍵字參數 # def fn2(a,*b,c): # print('a =',a) # print('b =',b) # print('c =',c) # 全部的位置參數都給a,b和c必須使用關鍵字參數 # def fn2(*a,b,c): # print('a =',a) # print('b =',b) # print('c =',c) # 若是在形參的開頭直接寫一個*,則要求咱們的全部的參數必須以關鍵字參數的形式傳遞 def fn2(*,a,b,c): print('a =',a) print('b =',b) print('c =',c) # fn2(a=3,b=4,c=5) # *形參只能接收位置參數,而不能接收關鍵字參數 # def fn3(*a) : # print('a =',a) # **形參能夠接收其餘的關鍵字參數,它會將這些參數統一保存到一個字典中 # 字典的key就是參數的名字,字典的value就是參數的值 # **形參只能有一個,而且必須寫在全部參數的最後 def fn3(b,c,**a) : print('a =',a,type(a)) print('b =',b) print('c =',c) # fn3(b=1,d=2,c=3,e=10,f=20) # 參數的解包(拆包) def fn4(a,b,c): print('a =',a) print('b =',b) print('c =',c) # 建立一個元組 t = (10,20,30) # 傳遞實參時,也能夠在序列類型的參數前添加星號,這樣他會自動將序列中的元素依次做爲參數傳遞 # 這裏要求序列中元素的個數必須和形參的個數的一致 # fn4(*t) # 建立一個字典 d = {'a':100,'b':200,'c':300} # 經過 **來對一個字典進行解包操做 fn4(**d)
3、返回值數據結構
# 返回值,返回值就是函數執行之後返回的結果 # 能夠經過 return 來指定函數的返回值 # 能夠之間使用函數的返回值,也能夠經過一個變量來接收函數的返回值 def sum(*nums): # 定義一個變量,來保存結果 result = 0 # 遍歷元組,並將元組中的數進行累加 for n in nums : result += n print(result) # sum(123,456,789) # return 後邊跟什麼值,函數就會返回什麼值 # return 後邊能夠跟任意的對象,返回值甚至能夠是一個函數 def fn(): # return 'Hello' # return [1,2,3] # return {'k':'v'} def fn2() : print('hello') return fn2 # 返回值也能夠是一個函數 r = fn() # 這個函數的執行結果就是它的返回值 # r() # print(fn()) # print(r) # 若是僅僅寫一個return 或者 不寫return,則至關於return None def fn2() : a = 10 return # 在函數中,return後的代碼都不會執行,return 一旦執行函數自動結束 def fn3(): print('hello') return print('abc') # r = fn3() # print(r) def fn4() : for i in range(5): if i == 3 : # break 用來退出當前循環 # continue 用來跳過當次循環 return # return 用來結束函數fn4 print(i) print('循環執行完畢!') # fn4() def sum(*nums): # 定義一個變量,來保存結果 result = 0 # 遍歷元組,並將元組中的數進行累加 for n in nums : result += n return result r = sum(123,456,789) # print(r + 778) def fn5(): return 10 # fn5 和 fn5()的區別 print(fn5) # fn5是函數對象,打印fn5實際是在打印函數對象 <function fn5 at 0x05771BB8> print(fn5()) # fn5()是在調用函數,打印fn5()其實是在打印fn5()函數的返回值 10
4、文檔字符串與做用域閉包
# help()是Python中的內置函數 # 經過help()函數能夠查詢python中的函數的用法 # 語法:help(函數對象) # help(print) # 獲取print()函數的使用說明 # 文檔字符串(doc str) # 在定義函數時,能夠在函數內部編寫文檔字符串,文檔字符串就是函數的說明 # 當咱們編寫了文檔字符串時,就能夠經過help()函數來查看函數的說明 # 文檔字符串很是簡單,其實直接在函數的第一行寫一個字符串就是文檔字符串 def fn(a:int,b:bool,c:str='hello') -> int: ''' 這是一個文檔字符串的示例 函數的做用:。。。。。 函數的參數: a,做用,類型,默認值。。。。 b,做用,類型,默認值。。。。 c,做用,類型,默認值。。。。 ''' return 10 help(fn)
# 做用域(scope) # 做用域指的是變量生效的區域 b = 20 # 全局變量 def fn(): a = 10 # a定義在了函數內部,因此他的做用域就是函數內部,函數外部沒法訪問 print('函數內部:','a =',a) print('函數內部:','b =',b) # fn() # print('函數外部:','a =',a) # print('函數外部:','b =',b) # 在Python中一共有兩種做用域 # 全局做用域 # - 全局做用域在程序執行時建立,在程序執行結束時銷燬 # - 全部函數之外的區域都是全局做用域 # - 在全局做用域中定義的變量,都屬於全局變量,全局變量能夠在程序的任意位置被訪問 # # 函數做用域 # - 函數做用域在函數調用時建立,在調用結束時銷燬 # - 函數每調用一次就會產生一個新的函數做用域 # - 在函數做用域中定義的變量,都是局部變量,它只能在函數內部被訪問 # # 變量的查找 # - 當咱們使用變量時,會優先在當前做用域中尋找該變量,若是有則使用, # 若是沒有則繼續去上一級做用域中尋找,若是有則使用, # 若是依然沒有則繼續去上一級做用域中尋找,以此類推 # 直到找到全局做用域,依然沒有找到,則會拋出異常 # NameError: name 'a' is not defined def fn2(): def fn3(): print('fn3中:','a =',a) fn3() # fn2() a = 20 def fn3(): # a = 10 # 在函數中爲變量賦值時,默認都是爲局部變量賦值 # 若是但願在函數內部修改全局變量,則須要使用global關鍵字,來聲明變量 global a # 聲明在函數內部的使用a是全局變量,此時再去修改a時,就是在修改全局的a a = 10 # 修改全局變量 print('函數內部:','a =',a) # fn3() # print('函數外部:','a =',a) # 命名空間(namespace) # 命名空間指的是變量存儲的位置,每個變量都須要存儲到指定的命名空間當中 # 每個做用域都會有一個它對應的命名空間 # 全局命名空間,用來保存全局變量。函數命名空間用來保存函數中的變量 # 命名空間實際上就是一個字典,是一個專門用來存儲變量的字典 # locals()用來獲取當前做用域的命名空間 # 若是在全局做用域中調用locals()則獲取全局命名空間,若是在函數做用域中調用locals()則獲取函數命名空間 # 返回的是一個字典 scope = locals() # 當前命名空間 print(type(scope)) # print(a) # print(scope['a']) # 向scope中添加一個key-value scope['c'] = 1000 # 向字典中添加key-value就至關於在全局中建立了一個變量(通常不建議這麼作) # print(c) def fn4(): a = 10 # scope = locals() # 在函數內部調用locals()會獲取到函數的命名空間 # scope['b'] = 20 # 能夠經過scope來操做函數的命名空間,可是也是不建議這麼作 # globals() 函數能夠用來在任意位置獲取全局命名空間 global_scope = globals() # print(global_scope['a']) global_scope['a'] = 30 # print(scope) fn4()
5、遞歸app
# 嘗試求10的階乘(10!) # 1! = 1 # 2! = 1*2 = 2 # 3! = 1*2*3 = 6 # 4! = 1*2*3*4 = 24 # print(1*2*3*4*5*6*7*8*9*10) # 建立一個變量保存結果 # n = 10 # for i in range(1,10): # n *= i # print(n) #求階乘 # range()是一個函數,能夠用來生成一個天然數的序列 # r = range(5) # 生成一個這樣的序列[0,1,2,3,4] # r = range(0,10,2) # r = range(10,0,-1) #輸出10,9,8,7,6,5,4,3,2,1 這10個數 # 經過range()能夠建立一個執行指定次數的for循環 # for()循環除了建立方式之外,其他的都和while同樣, # 包括else、包括break continue均可以在for循環中使用 # 而且for循環使用也更加簡單 # 將以前使用while循環作的練習,再使用for循環完成一次! # for i in range(30): # print(i) #輸出0-29的天然數 # 建立一個函數,能夠用來求任意數的階乘 def factorial(n): ''' 該函數用來求任意數的階乘 參數: n 要求階乘的數字 ''' # 建立一個變量,來保存結果 result = n for i in range(1,n): result *= i return result # 求10的階乘 # print(factorial(20)) # 遞歸式的函數 # 從前有座山,山裏有座廟,廟裏有個老和尚講故事,講的什麼故事呢? # 從前有座山,山裏有座廟,廟裏有個老和尚講故事,講的什麼故事呢?.... # 遞歸簡單理解就是本身去引用本身! # 遞歸式函數,在函數中本身調用本身! # 無窮遞歸,若是這個函數被調用,程序的內存會溢出,效果相似於死循環 # def fn(): # fn() # fn() # 遞歸是解決問題的一種方式,它和循環很像 # 它的總體思想是,將一個大問題分解爲一個個的小問題,直到問題沒法分解時,再去解決問題 # 遞歸式函數的兩個要件 # 1.基線條件 # - 問題能夠被分解爲的最小問題,當知足基線條件時,遞歸就不在執行了 # 2.遞歸條件 # - 將問題繼續分解的條件 # 遞歸和循環相似,基本是能夠互相代替的, # 循環編寫起來比較容易,閱讀起來稍難 # 遞歸編寫起來難,可是方便閱讀 # 10! = 10 * 9! # 9! = 9 * 8! # 8! = 8 * 7! # ... # 1! = 1 def factorial(n): ''' 該函數用來求任意數的階乘 參數: n 要求階乘的數字 ''' # 基線條件 判斷n是否爲1,若是爲1則此時不能再繼續遞歸 if n == 1 : # 1的階乘就是1,直接返回1 return 1 # 遞歸條件 return n * factorial(n-1) # print(factorial(10)) # 練習 # 建立一個函數 power 來爲任意數字作冪運算 n ** i # 10 ** 5 = 10 * 10 ** 4 # 10 ** 4 = 10 * 10 ** 3 # ... # 10 ** 1 = 10 def power(n , i): ''' power()用來爲任意的數字作冪運算 參數: n 要作冪運算的數字 i 作冪運算的次數 ''' # 基線條件 if i == 1: # 求1次冪 return n # 遞歸條件 return n * power(n , i-1) # print(power(8,6)) # # 練習 # 建立一個函數,用來檢查一個任意的字符串是不是迴文字符串,若是是返回True,不然返回False # 迴文字符串,字符串從前日後念和從後往前念是同樣的 # abcba # abcdefgfedcba # 先檢查第一個字符和最後一個字符是否一致,若是不一致則不是迴文字符串 # 若是一致,則看剩餘的部分是不是迴文字符串 # 檢查 abcdefgfedcba 是否是迴文 # 檢查 bcdefgfedcb 是否是迴文 # 檢查 cdefgfedc 是否是迴文 # 檢查 defgfed 是否是迴文 # 檢查 efgfe 是否是迴文 # 檢查 fgf 是否是迴文 # 檢查 g 是否是迴文 def hui_wen(s): ''' 該函數用來檢查指定的字符串是否迴文字符串,若是是返回True,不然返回False 參數: s:就是要檢查的字符串 ''' # 基線條件 if len(s) < 2 : # 字符串的長度小於2,則字符串必定是迴文 return True elif s[0] != s[-1]: # 第一個字符和最後一個字符不相等,不是迴文字符串 return False # 遞歸條件 return hui_wen(s[1:-1]) # def hui_wen(s): # ''' # 該函數用來檢查指定的字符串是否迴文字符串,若是是返回True,不然返回False # 參數: # s:就是要檢查的字符串 # ''' # # 基線條件 # if len(s) < 2 : # # 字符串的長度小於2,則字符串必定是迴文 # return True # # 遞歸條件 # return s[0] == s[-1] and hui_wen(s[1:-1]) print(hui_wen('abcdefgfedcba'))
6、高階函數函數式編程
# 高階函數 # 接收函數做爲參數,或者將函數做爲返回值的函數是高階函數 # 當咱們使用一個函數做爲參數時,其實是將指定的代碼傳遞進了目標函數 # 建立一個列表 l = [1,2,3,4,5,6,7,8,9,10] # 定義一個函數 # 能夠將指定列表中的全部的偶數,保存到一個新的列表中返回 # 定義一個函數,用來檢查一個任意的數字是不是偶數 def fn2(i) : if i % 2 == 0 : return True return False # 這個函數用來檢查指定的數字是否大於5 def fn3(i): if i > 5 : return True return False def fn(func , lst) : ''' fn()函數能夠將指定列表中的全部偶數獲取出來,並保存到一個新列表中返回 參數: lst:要進行篩選的列表 ''' # 建立一個新列表 new_list = [] # 對列表進行篩選 for n in lst : # 判斷n的奇偶 if func(n) : new_list.append(n) # if n > 5 : # new_list.append(n) # 返回新列表 return new_list # def fn4(i): # if i % 3 == 0: # return True # return False def fn4(i): return i % 3 == 0 # print(fn(fn4 , l)) # filter() # filter()能夠從序列中過濾出符合條件的元素,保存到一個新的序列中 # 參數: # 1.函數,根據該函數來過濾序列(可迭代的結構) # 2.須要過濾的序列(可迭代的結構) # 返回值: # 過濾後的新序列(可迭代的結構) # fn4是做爲參數傳遞進filter()函數中 # 而fn4實際上只有一個做用,就是做爲filter()的參數 # filter()調用完畢之後,fn4就已經沒用 # 匿名函數 lambda 函數表達式 (語法糖) # lambda函數表達式專門用來建立一些簡單的函數,他是函數建立的又一種方式 # 語法:lambda 參數列表 : 返回值 # 匿名函數通常都是做爲參數使用,其餘地方通常不會使用 def fn5(a , b): return a + b # (lambda a,b : a + b)(10,20) # 也能夠將匿名函數賦值給一個變量,通常不會這麼作 fn6 = lambda a,b : a + b # print(fn6(10,30)) r = filter(lambda i : i > 5 , l) # print(list(r)) # map() # map()函數能夠對可跌代對象中的全部元素作指定的操做,而後將其添加到一個新的對象中返回 l = [1,2,3,4,5,6,7,8,9,10] r = map(lambda i : i ** 2 , l) # print(list(r)) # sort() # 該方法用來對列表中的元素進行排序 # sort()方法默認是直接比較列表中的元素的大小 # 在sort()能夠接收一個關鍵字參數 , key # key須要一個函數做爲參數,當設置了函數做爲參數 # 每次都會以列表中的一個元素做爲參數來調用函數,而且使用函數的返回值來比較元素的大小 l = ['bb','aaaa','c','ddddddddd','fff'] # l.sort(key=len) #根據字符串的長度去排序 l = [2,5,'1',3,'6','4'] l.sort(key=int) #根據int型去排序 ,把每一個字符串轉成int去排序 # print(l) # sorted() # 這個函數和sort()的用法基本一致,可是sorted()能夠對任意的序列進行排序 # 而且使用sorted()排序不會影響原來的對象,而是返回一個新對象 l = [2,5,'1',3,'6','4'] # l = "123765816742634781" #這個字符串也能排序 print('排序前:',l) print(sorted(l,key=int)) print('排序後:',l)
閉包函數
# 將函數做爲返回值返回,也是一種高階函數 # 這種高階函數咱們也稱爲叫作閉包,經過閉包能夠建立一些只有當前函數能訪問的變量 # 能夠將一些私有的數據藏到的閉包中 def fn(): a = 10 # 函數內部再定義一個函數 def inner(): print('我是fn2' , a) # 將內部函數 inner做爲返回值返回 return inner # r是一個函數,是調用fn()後返回的函數 # 這個函數實在fn()內部定義,並非全局函數 # 因此這個函數老是能訪問到fn()函數內的變量 r = fn() # r() # 求多個數的平均值 # nums = [50,30,20,10,77] # sum()用來求一個列表中全部元素的和。一個函數 # print(sum(nums)/len(nums)) # 造成閉包的要件 # ① 函數嵌套 # ② 將內部函數做爲返回值返回 # ③ 內部函數必需要使用到外部函數的變量 #相似於Java中的封裝 def make_averager(): # 建立一個列表,用來保存數值 nums = [] # 建立一個函數,用來計算平均值 def averager(n) : # 將n添加到列表中 nums.append(n) # 求平均值 return sum(nums)/len(nums) return averager averager = make_averager() print(averager(10)) print(averager(20)) print(averager(30)) print(averager(40))
裝飾器ui
# 建立幾個函數 def add(a , b): ''' 求任意兩個數的和 ''' r = a + b return r def mul(a , b): ''' 求任意兩個數的積 ''' r = a * b return r # 但願函數能夠在計算前,打印開始計算,計算結束後打印計算完畢 # 咱們能夠直接經過修改函數中的代碼來完成這個需求,可是會產生如下一些問題 # ① 若是要修改的函數過多,修改起來會比較麻煩 # ② 而且不方便後期的維護 # ③ 而且這樣作會違反開閉原則(OCP) # 程序的設計,要求開發對程序的擴展,要關閉對程序的修改 # r = add(123,456) # print(r) # 咱們但願在不修改原函數的狀況下,來對函數進行擴展 def fn(): print('我是fn函數....') # 只須要根據現有的函數,來建立一個新的函數 def fn2(): print('函數開始執行~~~') fn() print('函數執行結束~~~') # fn2() def new_add(a,b): print('計算開始~~~') r = add(a,b) print('計算結束~~~') return r # r = new_add(111,222) # print(r) # 上邊的方式,已經能夠在不修改源代碼的狀況下對函數進行擴展了 # 可是,這種方式要求咱們每擴展一個函數就要手動建立一個新的函數,實在是太麻煩了 # 爲了解決這個問題,咱們建立一個函數,讓這個函數能夠自動的幫助咱們生產函數 def begin_end(old): ''' 用來對其餘函數進行擴展,使其餘函數能夠在執行前打印開始執行,執行後打印執行結束 參數: old 要擴展的函數對象 ''' # 建立一個新函數 def new_function(*args , **kwargs): print('開始執行~~~~') # 調用被擴展的函數 result = old(*args , **kwargs) print('執行結束~~~~') # 返回函數的執行結果 return result # 返回新函數 return new_function f = begin_end(fn) f2 = begin_end(add) f3 = begin_end(mul) # r = f() # r = f2(123,456) # r = f3(123,456) # print(r) # 向begin_end()這種函數咱們就稱它爲裝飾器 # 經過裝飾器,能夠在不修改原來函數的狀況下來對函數進行擴展 # 在開發中,咱們都是經過裝飾器來擴展函數的功能的 # 在定義函數時,能夠經過@裝飾器,來使用指定的裝飾器,來裝飾當前的函數 # 能夠同時爲一個函數指定多個裝飾器,這樣函數將會安裝從內向外的順序被裝飾 def fn3(old): ''' 用來對其餘函數進行擴展,使其餘函數能夠在執行前打印開始執行,執行後打印執行結束 參數: old 要擴展的函數對象 ''' # 建立一個新函數 def new_function(*args , **kwargs): print('fn3裝飾~開始執行~~~~') # 調用被擴展的函數 result = old(*args , **kwargs) print('fn3裝飾~執行結束~~~~') # 返回函數的執行結果 return result # 返回新函數 return new_function @fn3 @begin_end def say_hello(): print('你們好~~~') say_hello()