python函數(全局變量,局部變量,作用域,遞歸函數,高階函數,匿名函數)
1.1函數
1.1.1什麽是函數
函數就是程序實現模塊化的基本單元,一般實現某一功能的集合。
函數名:就相當於是程序代碼集合的名稱
參數:就是函數運算時需要參與運算的值被稱作為參數
函數體:程序的某個功能,進行一系列的邏輯運算
return 返回值:函數的返回值能表示函數的運行結果或運行狀態。
1.1.2函數的作用
- 函數是組織好的,可重復使用的,用來實現單一,或者相關功能的代碼。
- 函數能夠提高應用的模塊性,和代碼的重復利用率
我們已近知道python提供了許多內置函數,比如print(),我們自已創建的函數稱為內置函數
1.1.3定義函數的語法
def 函數名(參數): 函數體 return 返回值內容
1.1.4函數定義規則
- 函數代碼以def關鍵詞開頭,後接定義函數的名稱和圓括號,冒號():
- 函數內容以冒號":"開始,並且縮進
- 函數內容的第一行內容可選擇性使用文檔字符串---用來定義該函數的說明
- 函數的返回值: return [返回值內容] 用於結束函數,返回一個值,表示程序執行的結果。
- 函數不帶return 默認返回None 返回值可以是任何類型的數據(數字,字符串,函數,列表,元祖,字典等),也可以是一個表達式
- 函數參數:任何傳入參數和自變量必須放在圓括號中間,圓括號之間用於定義參數。
1.1.5函數調用
定義函數語法
def printinfo(): print("hello world") return
調用函數
printinfo() #函數執行結果 hello world
查看函數返回值
print(printinfo()) #結果 hello world None #默認函數值返回類容
其他返回值示例
def printinfo(): print("hello world") return [111+222] print(printinfo()) #結果 hello world [333] #返回值內容
1.1.5函數參數
註:形參和實參(定義函數時,圓括號(參數)中的所有參數都是形式參數也稱為形參,調用函數中,圓括號(參數)中的參數稱為實際參數,也叫實參)
1)必須參數::
2)關鍵字參數:
3)默認參數:
4)可變參數(*args,**kwargs):
1.必須參數:
從字面理解:必須要傳入參數
傳入的參數:與定義的形參順序一一對應
def stuinfo(name,age): print(name,age) return #在不傳入參數 stuinfo() #調用函數 #函數執行結果 TypeError: stuinfo() missing 2 required positional arguments: ‘name‘ and ‘age‘ #報錯,提示類型錯誤,該函數,缺少兩個位置參數不傳入參數
def stuinfo(name,age): print(name,age) return stuinfo("zhangsan",18) #函數執行結果 zhangsan 18傳入參數
2.關鍵字參數
def stuinfo(name,age,hobby): print(name,age,hobby) return #參數位置匹配,關鍵字參數,與形參的位置順序無關, stuinfo(age=19,name="lisi",hobby="run") #name= age= hobby=就是關鍵字參數 #函數執行結果 lisi 19 run關鍵字參數
3.默認參數
默認參數必須指向不變的對象
當函數有多個參數,把變化大的參數反正前面,變化小的參數放在後面。變化小的參數就作為默認參數。
默認參數好處:降低調用函數的難度
#默認參數,可以直接使用用,也可以修改默認參數值 def grade(name,age,city="BeiJing"): #city="BeiJing" 就是默認參數 print(name,age,city) # grade("yangjian",age=18) grade("lala",age=18,city="shanghai") #grade函數執行結果 lala 18 shanghai默認參數
4.可變參數
*args **args
用途:在定義函數需要,每個定義函數功能的都可以繼續優化,所以我們需要用動態參數
如果把參數寫死了,後期繼續修改完善的功能的,修改該函數功能則會相當麻煩
*args 結果類型是元祖,傳遞值是任意類型
def test(*args): print(args) test(123456,[12,45,88],{"name":"qw","age":15}) #可以傳遞任意參數的類型 #函數執行結果 (123456, [12, 45, 88], {‘name‘: ‘qw‘, ‘age‘: 15})
**kwargs結果類型是字典,傳遞值是以key=value方式傳入
def test1(**kwargs): print(kwargs) test1(name="xiha",age="12") #執行結果 {‘age‘: ‘12‘, ‘name‘: ‘xiha‘}
函數* 和 ** 解包
#* def test(*args): print(args) test(*[1,2,3,4,5,6,7,8,9]) #*參數解包,把【元素】 循環出來,添加到元祖中 #結果 (1, 2, 3, 4, 5, 6, 7, 8, 9)*
def test1(**kwargs): print(kwargs) test1(**{"hobby":456,"number":789}) #**參數解包,把key:value 循環出來,添加到字典中 #結果 {‘number‘: 789, ‘hobby‘: 456}**
函數參數組合
def f2(a, b, c=0,*args,**kwargs): print(‘a =‘, a, ‘b =‘, b, ‘c =‘, c, args,kwargs) f2(12,b=12,c=89,aa="as",bb="xxx") #結果 a = 12 b = 12 c = 89 () {‘bb‘: ‘xxx‘, ‘aa‘: ‘as‘}
函數參數總結:
1.形參的位置順序,必須與實參的順序一一對應,缺一不行,多一不行
2.關鍵字參數,無須一一對應,缺一不行,多一不行
3.位置參數必須在關鍵字參數左邊
4.默認參數一定要用不可變對象,如果是可變對象,程序運行時會有邏輯錯誤
1.2全局變量和局部變量
在子程序中定義的變量稱為局部變量,只在子程序內部生效,
在程序一開始定義的變量稱為全局變量
全局變量作用域是整個程序,局部變量作用域是定義該變量的子程序。
當全局變量與局部變量同名時:在定義局部變量的子程序內,局部變量起作用,在其他地方全局變量起作用
name = "xixi" #全局變量 def change_name(): name = "haha" #局部變量只在函數局部作用域內生效 print("我的名字",name) return change_name() print(name) def me(): global name #聲明name是全局變量 global name = "yj" #修改name全局變量的值 print(name) return me()
如果全局變量是可變的數據類型,函數可以對全局變量內部直接進行修改
eng = ["merry","jack","petter"] def chang(): eng.append("mali") print(eng) return chang()
總結:
一般寫程序變量的命名規則
###全局變量變量名大寫
###局部變量變量名小寫
- 函數優先讀取局部變量,能讀全局變量,無法對全局變量重新賦值操作,#全局變量是不可變的類型
- 全局變量是可變類型,函數可以對全局變量進行操作
- 函數中有global關鍵字,變量本質就是全局變量,可讀取全局變量,也可操作全局變量
1.3函數之間嵌套
name = "YangJIan" #最外層 def change_name(): #第二層 name = "YangJIan2" def change_name2(): #第三層 name = "YangJIan3" print("第3層打印", name) change_name2() # 調用內層函數 print("第2層打印", name) change_name() #先執行局部函數的打印, print("最外層打印", name) # 第3層打印 YangJIan3 # 第2層打印 YangJIan2 # 最外層打印 YangJIan
註:多層函數嵌套,子級函數,只在子級函數內部生效。父級函數能調用子級函數的功能
1.4遞歸函數
1.在函數內部,可以調用其他函數,如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
2.在使用遞歸策越是,必須有一個明確的敵對結束條件,稱為遞歸出口
函數調用的時候,每次調用時要做地址保存,參數傳遞等。
如果函數包含了對其自身函數的調用,該函數就是遞歸。如下
def foo(n): #實現階乘 if n == 1: return n # 當滿種條件n==1才執行return 操作 res = n*foo(n-1) #調用自已本身的函數的結果(再判斷條件是否滿足條件)給res , return res print(foo(5)) #120
遞歸算法所所體現的重復一般有的要求:
1.每次調用在上次規模上都有有所減小:
2.每次遞歸調用都是有明確條件的。
3.相領兩次的重復之間有緊密的聯系,前一次要為後一次做準備(通常前一次的輸出(返回值結果))就作為後一次的輸入;
4.在問題的規模得到滿足條件時,而不再進行遞歸調用。
1.5函數式編程
面向過程編程:我們通過把大段代碼拆成函數,通過一層一層的函數,可以把復雜的任務分解成簡單的任務,這種一步一步的分解可以稱之為面向過程的程序設計。函數就是面向過程的程序設計的基本單元。
函數式編程:是使用一系列函數去解決問題,函數式編程就是根據編程的範式來的出想要的結果,只要是輸入時確定的,輸出就是確定的。
1.6高階函數
能把函數作為參數傳入,這樣的函數就稱為高階函數。
回到頂部1.6.1函數即變量
以python的內置函數print()為列,調用該函數一下代碼
>>> print("hello world") hello world #只寫print >>> print <built-in function print> #可見print("hello world")是函數調用,而print是函數本身
要獲得函數調用執行的結果,我們把結果賦值給變量:
>>> aa = abs(-20) >>> aa 20
如果把函數本身賦值給變量
>>> p = print >>> p <built-in function print> #函數本身可以賦值給變量,變量可以指向函數
我們通過變量來調用這個print函數,驗證結果如下
>>> p("check") check
總結:函數名也是變量,對於print()這個內置函數,完全可以把函數名print看成變量,它指向一個可以打印任何東西的函數
註:實際程序代碼絕不能這麽寫,上面只是為了舉例說明,要恢復print函數,請重啟python的交互環境
回到頂部1.6.2傳入函數
變量可以指向函數,函數的參數能接收變量,那麽一個函數就可以接收另一函數作為函數,這種函數就稱為高階函數,
函數的返回值是一個函數名,也是高階函數。
例如:一個簡單的高階函數
def add(x,y,z): return abs(x)+abs(y) aa = add(12,23,abs) #函數執行的結果 賦值給 aa print(aa) #查看aa的值 #35 #註,abs()函數是求一個整數的絕對值
1.7匿名函數
什麽是匿名函數:
在python中有一個匿名函數lambda,匿名函數就是指:無需定義標識符(函數名)的函數或子程序。
定義lambda表達式:
lambda arguments:express #arguments 參數(可以有多個參數) #express 表達式 #lambda返回值是一個函數的地址,也就是函數對象 aa = lambda arguments:express #把的到lambda函數地址,賦值給變量aa 查看這個lambda函數地址 ,用aa(argument) 查看這個函數的值
例1
def pf(x=0): return x**2 print(pf(3))普通函數定義,求數字平方
aa = lambda x:x**2 print(aa(4)) #16lambda函數,求數字平方
總結:
1.lambda函數可以參數可以有多個,包含的表達式不能超過一個,不要試圖向lambda函數中塞入太多東西,如果你需要做復雜的功能,應該定義一個普通函數,想定義什麽就定義什麽。
2.lambda函數用在需要封裝特殊的,非重用代碼上,避免令我們的代碼充斥大量的單行函數。
map函數
map()函數,map映射
map(func,iterable)
map()函數接受兩個參數,一個是函數,一個可叠代的對象(iterable),map將傳入的函數依次作用到序列的每個元素,並把結果作為新的 可叠代的對象 的結果返回
例:有個函數,f(x) = x+1 把得到的數字 加1 要把這個函數作用在一個[1,2,3,4,5,6]上
number = [1,2,3,4,5,6] #1.用普通函數定義方法 def add_one(x): return x+1 def map_test(func,arrey): res = [] for i in arrey: i = func(i) res.append(i) return res print(map_test(add_one,number)) #[2, 3, 4, 5, 6, 7] #2.用lambda函數定義的得到結果,借助1定義的map_test函數 print(map_test(lambda x:x+1,number)) #[2, 3, 4, 5, 6, 7] #3.用map()本身函數去定義 print(list(map(lambda x:x+1 ,number))) #[2, 3, 4, 5, 6, 7] #註:map()得出的結果是一個iterator ,需要用list()函數讓它個整個序列都計算出來返回一個list
我們可能會想,寫一個循環,也可以計算出結果,但要實現多個功能,是不是也要寫多個循環 例:得出每個列表中元素的平方或則n次方
map()作為高階函數,事實上把運算規則抽象了,不但可以計算簡單的 f(x) = x+1 ,也能計算更復雜的函數。
總結:map() 處理序列中的每個元素,得到的結果是一個 iterator ,需通過list(iteratro),該list元素個數,與原來位置一樣
reduce函數
在python2可以直接用reduce()函數
在python3需要調用reduce模塊
from functools import reduce reduce(function, sequence, initial=None) #該函數的默認用法
reduce函數,將function作用sequence序列的元素,每次攜帶一對(先前的結果以及下一序列的元素),連續的將現有的結果和下一個作用在獲得的隨後的結果上,最後得到我們的序列為一個最終結果的返回值
number1 = [2,3,4,10] #1.普通函數定義 def chengfa(x,y): return x*y #返回得到兩個數相乘的結果 def reduce_test(func,seq,init=None): if init is None: res = seq.pop(0) #seq刪除第一個元素,並獲取刪除這個元素 賦值給res else: res = init for i in seq: res = func(res,i) #循環一次,執行func這個函數 return res print(reduce_test(chengfa,number1)) #240 print(reduce_test(chengfa,number1,10)) #2400 #如果給了init 初始值,就是從初始值 乘以列表的每個元素的的出結果 #2.lambda函數,借助reduce_test()函數定義 print(reduce_test(lambda x,y:x*y,number1,init=3)) #720 #3.使用reduce(),結合lambda() print(reduce(lambda x,y:x*y, number1)) #240 得到列表所有元素,相乘的結果 number1 = [2,3,4,10] #1.普通函數定義 def chengfa(x,y): return x*y #返回得到兩個數相乘的結果 def reduce_test(func,seq,init=None): if init is None: res = seq.pop(0) #seq刪除第一個元素,並獲取刪除這個元素 賦值給res else: res = init for i in seq: res = func(res,i) #循環一次,執行func這個函數 return res print(reduce_test(chengfa,number1)) #240 print(reduce_test(chengfa,number1,10)) #2400 #如果給了init 初始值,就是從初始值 乘以列表的每個元素的的出結果 #2.lambda函數,借助reduce_test()函數定義 print(reduce_test(lambda x,y:x*y,number1,init=3)) #720 #3.使用reduce(),結合lambda() from functools import reduce print(reduce(lambda x,y:x*y, number1)) #240得到列表所有元素,相乘的結果
print(reduce(lambda x,y:x+y,range(1,101)))得到1-100的和
filter函數
filter()函數用於過濾序列
和map()類似,filter()也接受一個函數和一個序列(可叠代的對象,也就是能被for循環),和map()不同的是,fillter()把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。
例:
aa = [‘A‘, ‘‘, ‘B‘, None, ‘C‘, ‘ ‘] #1.自定義函數測試 def not_empty(s): return s and s.strip() def filter_test(func,iter): res = [] for i in iter: i = func(i) if i: res.append(i) return res print(filter_test(not_empty,aa)) #[‘A‘, ‘B‘, ‘C‘] #2.filter內置函數測試 print(list(filter(not_empty,aa))) #[‘A‘, ‘B‘, ‘C‘]把列表中空字符串,空元素,都去掉
filter()這個函數,關鍵在於正確實現一個篩選函數,
註:filter()函數返回的是一個iterator,內存地址,需要看內存地址的值, 用list()函數或得該地址的值
sorted函數
sorted()函數也是一個高階函數,它可以接收key
sorted排序,排序是比較元素的大小,如果是數字可以直接比較,如果是字符串或則兩個dict(字典)?
sorted()傳入的參數是可叠代的對象,返回值的對象是一個列表
例:
aa = [11,-10,20,21,30,-40] print(sorted(aa))數字默認排序
接收一個key函數來實現自定義排序
例:根據絕對值大小來進行排序
aa = [11,-10,20,21,30,-40] print(sorted(aa,key=abs)) #[-10, 11, 20, 21, 30, -40]根據絕對值排序
例:字符串排序
print(sorted("hello")) #[‘e‘, ‘h‘, ‘l‘, ‘l‘, ‘o‘] print(sorted(["hello","ho","haha"])) # [‘haha‘, ‘hello‘, ‘ho‘]字符串排序
註:默認情況下,對字符串排序是按照ASCII編碼表的大小進行比較的
最後總結:
python內置的幾個高階函數:map() ,reduce(),filter,sorted()
python函數(全局變量,局部變量,作用域,遞歸函數,高階函數,匿名函數)