Python函數的定義的方法: ...
我們前面學的都是面向過程式的編程(代碼從上到下寫,並運行),而函數式編程是將繁雜的代碼通過整理特性進行規整。像圖書館一樣,將小說、教學、外文等書籍進行分類。讓編程人員或看代碼人員很容易的查看該段代碼的特性。並且可以重覆的利用該段代碼進行相同的操作、像圖書館書是公用的一樣,誰都可以去看,這樣又能便於觀察又能重覆使用,是日後主要使用的技術。
def test(): #設置一個函數名稱 #test #註釋函數作用 print('函數式編程') #函數程式處理邏輯 return 0 #定義返回值,如果沒有定義將返回None test() #調用
函數:
1. 可以在調用時,在括弧內放入參數,裡面有(位置參數,關鍵字參數,混合參數,預設參數,彙總參數)
2. 位置參數必須要在關鍵字參數前面。
位置參數:
將參數一一對應,並傳入到函數中:
def func(a,b,c): print(a,c,b) func(1,2,3) #將1=>a,2=>b,3=>c
關鍵字參數:
將特定的參數以賦值形式對應起來。
def func(a,b,c): print(a,c,b) func(a=1,b=2,c=8)
混合參數(位置參數與關鍵字參數)
不能寫在位置參數的前面:
def func(a,b,c): print(a,c,b) test(3,b=2,c=1) √ test(c=1,2) X
預設參數:
調用函數的時候,預設參數非必須傳遞。
如果再沒有定義其他,就以預設的來。
在定義預設值時:不可變類型隨便傳,但可變類型需要註意:
def test(x,y=1): print(y) test(2)
彙總參數:
1. *args:接收N個位置參數,轉換成元組的形式。
def test1(x,*args): print(x) print(args) test1(1,2,3,4,5,6) & test1(1,*[2,3,4,5,6]) #這裡的*後面的列表是將列表內容提取並重新賦值給元組,以元組形式輸出。
2. **kwargs:把N個關鍵字參數,轉換成字典的方式。
def test1(x,**kwargs): print(x) print(kwargs) # test1(1,name='3',age=20) test1(2,name='xuan',age=9) test1(1,**{'name':"xuan",'age':20}) #將字典內容提取,在重新賦值給函數內的kwargs 2 {'name': 'xuan', 'age': 9} 1 {'name': 'xuan', 'age': 20}
作用域:
在Python中,一個函數就是一個作用域,而所有的函數都掛靠在.py文件的總作用域中。
在這裡寫的代碼分為:全局作用域和局部作用域
1. 全局作用域
- 要使用全大寫形式定義變數名稱,為了將局部變數做標識。
- 局部作用域全部掛靠在全局作用域內
NAME = 'a1' def func(): name = 'a2' print(name) print(NAME) func()
2. 局部作用域:
- 局部作用域可以調用全局作用域。局部和局部,全局和局部不能調用。
- 局部作用域之間無法調用,可以使用父作用域。
def change_name(name): #一個函數叫做一個作用域, print('before change',name) name='XB' print('after change',name) name = 'xb' change_name(name) print(name)
global:
在局部作用域中,進行全局作用域內容的查找,並可以修改。(只能修改可變類型,不可變類型為局部內重新賦值)
NAME = 'a1' def func(): global NAME NAME = 'bbb' func() print(NAME)
nonlocal:
在子局部作用域中,進行對父級局部作用域的內容查找不更改全局作用域內容,並可以修改。(同樣修改可變類型,不可變類型為局部內重新賦值)
NAME = 'a1' def func(): NAME = '111' def func2(): nonlocal NAME NAME = 'bbb' func2() print(NAME) func() print(NAME)
返回值:
在函數中使用return來進行函數這個子作用域的返回值的關鍵字。其中:
1. 預設如果沒有return,但定義了取值變數的話,預設為None。
def papa(): print('111') a = papa() #a就是None
2. 如果只有一個返回值,那麼就返回相應的數值和數據類型。如果有多個返回值,那麼將返回一個元組。
def papa(): dic = {'k1':'v1'} return dic #將返回字典類型 return {'k1':'v1'},{'k1':'v1'},{'k1':'v1'} #將返回元組類型 a = papa() print(a)
3. 返回值同樣可以返回另一個函數的記憶體對象地址
def a(): print('111') def papa(): return a #返回一個記憶體對象地址,返回後可以直接運行。 b = papa() b()lam
lambda
用於表示簡單的函數時,可以使用lambda來進行,其中需要註意的:
1. 只能用一行來表示lambda
2. 只能用參數傳的值,而不能自己定義一個變數。
使用lambda預設就會有一個return。
a = lambda : 1+1 #將1+1的結果返回到a中。 print(a())
lambda可以搭配三元運算使用:
a = lambda : 1 if 1+1 == 2 else 2 #判斷如果1+1等於2的話,那麼返回1,否則返回2 print(a())
其他:
lis = lambda : [ i for i in range(10) if i%3 == 0 ] #迴圈1-10,判斷其中哪些為3整除,添加到列表中,生成一個lambda。 print(lis())
閉包:
閉包是嵌套在函數中的函數,而閉包必須是內層函數對外層函數的變數(非全局變數)的引用。
為函數創建一個區域(內部變數供自己使用)為以後執行提供數據。
#實例 li = [] def func(new_value): li.append(new_value) total = sum(li) return total/len(li) print(func(1000)) print(func(2000)) print(func(5000)) #那麼問題來了,li是全局變數,我可以在全局隨意更改。 li = [] def func(new_value): li.append(new_value) total = sum(li) return total/len(li) print(func(1000)) print(func(2000)) li.append(120301) print(func(5000)) #得出來的結果就大不相同了。如何有解決辦法,那就是把li放到函數的小作用域里。 def func(new_value): li = [] li.append(new_value) total = sum(li) return total/len(li) print(func(1000)) print(func(2000)) print(func(5000)) #這樣一來,每次運行函數,就會新生成一個新的列表出來。那麼值就沒有變化了。接下來如何解決這個問題。閉包就來了。 def func(): li = [] def func1(new_value): li.append(new_value) total = sum(li) return total/len(li) return func1 func = func() print(func(1000)) print(func(2000)) print(func(5000)) #這樣一來,即不會出現全局模式更改的問題,也不會擔心列表重新生成的問題。這就是比好。
其中,下麵一塊區域叫做閉包:
li = [] def func1(new_value): li.append(new_value) total = sum(li) return total/len(li)
我們可以通過查看函數內是否有自由變數來側面證明是不是閉包。
def func(): li = [] def func1(new_value): li.append(new_value) total = sum(li) return total/len(li) return func1 func = func() print(func.__code__.co_freevars) #查看函數的自由變數 #('li',) print(func.__code__.co_varnames) #查看函數的局部變數 #('new_value', 'total')
函數需要註意的點:
1. 根據數據類型的不同,有些是有返回值的,有些是沒有返回值的。
lis = [] def a(): return lis.append('111') b = a() print(b) #由於列表形式的添加是沒有返回值的,所以返回的是None
lis = '123' def a(): return lis + '321' b = a() print(b) #由於字元串是有返回值的,所以返回的是所需值
2. 需要判斷是傳入函數記憶體地址還是函數的return的值。
def func(): print('1111') return 0 lis = [func,func,func] #這裡傳入的是函數的記憶體地址。不是執行結果。 for item in lis: print(item) #列印的是記憶體地址 ''' <function func at 0x000001D8489B9550> <function func at 0x000001D8489B9550> <function func at 0x000001D8489B9550> '''
def func(): return 0 lis = [func(),func(),func()] #這裡傳入的是運行完func的返回值 for item in lis: print(item) #列印的是0 ''' 0 0 0 '''
3. 閉包問題
def func(name): v = lambda x:x+name return v v1 = func('武沛齊') v2 = func('alex') v3 = v1('銀角') v4 = v2('金角') print(v1,v2,v3,v4)
result = [] for i in range(10): func = lambda : i # 註意:函數不執行,內部代碼不會執行。 result.append(func) print(i) print(result) v1 = result[0]() v2 = result[9]() print(v1,v2)
def func(num): def inner(): print(num) return inner result = [] for i in range(10): f = func(i) result.append(f) print(i) print(result) v1 = result[0]() v2 = result[9]() print(v1,v2)
4. 實參與形參
傳參的類型是否為可變類型,如果可變類型,那麼實參也會一起改變,如果是不可變類型,那麼就會重新創建一個方法內部變數。
###########傳入不可變類型############ def func(a): a += 1 print(id(a)) a = 7 func(a) print(id(a)) print(a) #判斷ID輸出是否一樣,a輸出的結果? ############傳入可變類型################### def func(a): a[0] = 8 print(id(a)) a = [1,2,3] func(a) print(id(a)) print(a) #判斷ID輸出是否一樣,a輸出的結果?
函數中的那些坑:
1. 函數中有定義一個空列表作為形參:
def a(b,lis=[])
問題:在沒有傳入新的列表當做實參時,將會使用定義好的空列表,那麼將會導致多個沒有定義的實參重覆調用該空列表。這將導致數據內容不一致的問題。
def b(a,lis=[]): for i in range(a): lis.append(i*i) print(lis) b(3) b(3) b(3) ''' [0, 1, 4] [0, 1, 4, 0, 1, 4] [0, 1, 4, 0, 1, 4, 0, 1, 4] '''
可以傳參時傳入一個自己的空列表,那麼就將使用自己傳入的空列表。
def b(a,lis=[]): for i in range(a): lis.append(i*i) print(lis) b(3,[]) b(3,[]) b(3,[]) ''' [0, 1, 4] [0, 1, 4] [0, 1, 4] '''
如何解決這類問題,要麼就刪除空列表,要麼就進行判斷。
def b(a,lis=[]): if lis: lis = [] for i in range(a): lis.append(i*i) print(lis) b(3,[]) b(3,[]) b(3) b(3) b(3) ''' [0, 1, 4] [0, 1, 4] [0, 1, 4] '''