函數
1.什么是函數:
函數就是執行特定任何以完成特定功能的一段代碼
2.為什么需要函數
- 復用代碼
- 隱藏實現細節
- 提高可維護性
- 提高可讀性便於調試
3.函數的創建和調用
- 函數的創建
- 函數的定義和調用
#函數的創建和調用 def calc_1(): print("這是一個無返回值的函數") calc_1()
- 函數的返回值
- 函數使用return語句返回值。定義函數時是否需要return語句,需要視情況而定
- 如果函數沒有return語句,Python將認為函數以return None結束,即返回空
- 函數可以用return語句,返回一個值
- 函數也可以用return語句返回多個值,多個值以元組類型保存
- 當函數返回多個值時,我們可能需要單獨對返回值進行處理,那么我們可以不采用元組的形式接受數據,可使用多變量賦值的形式
-
def calc_2(a,b): c = a + b return c x = calc_2(99,1) print(x) def calc_3(a,b): return a,b x = calc_3(1,2) print(x,type(x)) num_1,num_2 = calc_3(8,9) #多變量賦值的形式,接受函數內部傳遞過來的值,但是變量個數要與返回值個數相同 print(num_1,num_2)
4.函數的參數傳遞
- 實參和形參
- 實參:調用函數時向其傳遞實參,根據不同參數類型,將實參的值或引用傳遞給形參
- 形參:定義函數時,圓括號內的參數為形參。
- 參數的類型可分為固定數據類型:(如整數、浮點數、字符串、元組等)和可變數據類型(如列表、字典、集合等)
- 當參數類型為固定數據類型時,在函數內部直接修改形參的值不會影響實參
-
- 形參和實參的名字可以不相同
def calc_2(a): #函數名括號內的a時形參 a = 3 #當形參的類型為可變數據類型時,在函數內部直接修改值不會影響實參 return a b = 2 c = calc_2(b) #調用函數時,實際傳送的b是實參 print(c) #3 print(b) #2 def calc_1(a): #函數名括號內的a時形參 a.append(1) #當形參的類型為可變數據類型時,在函數內部直接修改值會影響實參 return a list_1 = [1,2,3] print(list_1) #[1, 2, 3] b = calc_1(list_1) #調用函數時,實際傳送的list_1是實參 print(b) #[1, 2, 3, 1] print(list_1) #[1, 2, 3, 1]
- 函數參數傳遞的內存分析:
def fun(arg1,arg2): print(arg1) #11 print(arg2) #[22, 33, 44] arg1 = 100 arg2.append(10) n1 = 11 n2 = [22,33,44] print("調用函數之前n1和n2的值:",n1,n2) #調用函數之前n1和n2的值: 11 [22, 33, 44] fun(n1,n2) print("調用函數之后n1和n2的值:",n1,n2) #調用函數之后n1和n2的值: 11 [22, 33, 44, 10]
- 參數類型:Python中,有多種參數類型,包括:
- 位置參數
- 實參和形參的順序必須嚴格一致
- 實參和形參的數量必須相同
-
#定義函數 def calc_1(a,b,c): print(a,b,c) #調用函數 calc_1(1,6,5) #calc_1(4,5)
- 默認值參數
- 函數在定義時,給形參設置默認值,只有與默認值不符時,才需要傳遞實參。
-
#函數的默認參數 def calc_1(a,b = 10): print(a,b) calc_1(10) #10 10 calc_1(10,100) #10 100
- 關鍵字參數
- 按參數名字傳遞值的方式
- 關鍵字參數允許函數調用時參數的順序與定義時不一致
-
#關鍵字參數傳遞 def calc_1(a,b,c): print("a的值為:",a,"b的值為:",b,"c的值為:",c) calc_1(b = 1,c = 2 , a = 3) #a的值為: 3 b的值為: 1 c的值為: 2
- 不定長參數
- 定義函數時,一個函數可以同時有一個個數可變的位置參數和一個個數可變的關鍵字形參,但是不能同時存在一個以上的個數可變的位置參數或者一個以上的個數可變的關鍵字形參
- 個數可變的位置參數:定義函數時,無法確定實參的個數時,可使用可變的位置參數。(接收到的實參存儲在元組中)
-
#不定長參數 def calc_1(*args): #可變的位置參數 print(args) calc_1(1) #(1,) calc_1(1,2,3) #(1, 2, 3) calc_1(1,5,6,7,8,7) #(1, 5, 6, 7, 8, 7)
-
個數可變的關鍵字形參:定義函數時,無法事先確定傳遞的關鍵字實參的個數時,使用可變的關鍵形參。(接收到的實參存儲在字典中)
-
def fun(**args): #可變的關鍵字參數 print(args) fun(a = 1) #{'a': 1} fun(a = 1 , b = 2 , c = 3) #{'a': 1, 'b': 2, 'c': 3}
- 位置參數
5.函數的特殊調用,與參數順序問題
def fun(a,b,c): print(a,b,c) list_1 = [1,4,7] fun(*list_1) #在調用函數時,將列表的每一個元素都轉換為位置實參 注意:列表的元素要與形參相同 tuple_1 = [2,5,8] fun(*tuple_1) #在調用函數時,將元組的每一個元素都轉換為位置實參 注意:元組的元素要與形參相同
def fun(a,b,c): print(a,b,c) dict_1 = { "a":100, "b":200, "c":300 } fun(**dict_1) #在調用函數時,將字典的每鍵值對都轉換為關鍵字實參 注意:字典的鍵名要與形參名相同
def fun(a,b,c,d): print(a,b,c,d) fun(10,20,30,40) #位置參數 fun(a=10,c=20,b=60,d=100) #關鍵字參數 fun(10,40,c=20,d=20) #前面使用位置參數,后面使用關鍵字參數。注意,不能將位置調換 def fun1(a,b,*,c,d): #規定*號后面的形參,必須使用關鍵字參數 print(a,b,c,d) #fun1(10,20,30,40) #位置參數,報錯,因為c和d沒有使用關鍵字形參 fun1(a=10,c=20,b=60,d=100) #關鍵字參數 fun1(10,40,c=20,d=20) #前面使用位置參數,后面使用關鍵字參數。注意,不能將位置調換
''' 定義函數時:形參的順序問題 ''' def fun1(a,b,*,c,d,**kwargs): pass def fun2(*args,**kwargs): pass def fun1(a,b=10,*args,**kwargs): pass
6.變量的作用域
- 程序代碼能夠訪問該變量的區域
- 根據變量的有效范圍,可以分為
- 全局變量:函數體外定義的變量,可作用於函數內外。
-
name = "Python" #變量name為全局變量,所以這個變量在函數體的內外都可以使用 print("這是是函數體外的輸出:",name) def fun1(): print("這里是函數體內的輸出",name) fun1()
#由下列代碼可見,函數體內的修改全局變量的值,只能在函數體內使用,不能作用於函數體外。 name = "Python" #變量name為全局變量,所以這個變量在函數體的內外都可以使用 print("這是是函數體外的輸出1:",name) #這是是函數體外的輸出1: Python def fun1(): #print("這里是函數體內的輸出1",name) #此行代碼會報錯,因為函數體內有修改全局變量的值 name = "HelloWorld" #當我們使用這行代碼是,表示:定義了一個局部變量,因此上面這行代碼會報錯。 print("這是是函數體內的輸出2",name) #這是是函數體內的輸出2 HelloWorld fun1() print("這是是函數體外的輸出2",name) #這是是函數體外的輸出2 Python
- 局部變量:在函數內定義的並使用的變量,只在函數內部有效,局部變量在使用global聲明,這個變量就會成全局變量。
-
#局部變量 def fun(a,b): c = a + b #c是在函數內部聲明的變量,所以c為局部變量;a b 也屬於局部變量。作用范圍僅限函數內部。 return c print(fun(10,20)) #print(a,b,c) #代碼會報錯,因為abc都屬於局部變量,作用范圍都值在函數內部。
#global:在函數體內聲明全局變量 num = 1 def fun(): global num #函數內部定義的變量,是局部變量,在使用global聲明時,這個變量就變成了全局變量 global num1 num = 2 num1 = 3 print("fun函數體內的num的值:",num) print("fun函數體內的num1的值:",num1) fun() print("fun函數體外的num的值:", num) print("fun1函數體外的num1的值:", num1)
#nonlocal:在函數體內嵌套一個函數,要修改嵌套函數中變量的作用域,可使用nonlocal關鍵字 def fun1(): num1 = 1 def fun2(): nonlocal num1 #nonlocal num2 #此處會報錯,因為外變量中沒有num2 global num3 #也可以在嵌套函數的內部聲明全局變量 num1 = 2 num3 = 30 print("這是fun2中的num1",num1) print("這是fun2中的num3",num3) fun2() print("這是fun1中的num1",num1) print("這是fun1中的num3",num3) fun1() #print("這是函數外的num1",num1) #此處代碼會報錯,因為nonlocal不能將局部變量變為全局變量 print("這是函數外的num3",num3)
7.遞歸函數
- 什么是遞歸函數:
- 如果在一個函數的函數體內調用了該函數本身,這個函數就成為遞歸函數。
-
def fun(): print("這是一個遞歸函數") return fun() fun()
- 遞歸的組成部分
- 遞歸調用與遞歸終止條件
- 遞歸調用過程
- 每遞歸調用一次函數,都會在棧內存分配一個棧幀
- 每執行完一次函數,都會釋放相應的空間
遞歸的優缺點
- 缺點:占用內存多,效率低下
- 優點:思路和代碼簡單
- 例題:6的階乘
-
-
def fun(num): if num==1: return 1 #出口 else: return num * fun(num - 1) #fun函數內調用fun print(fun(6)) #720
-
- 斐波那契數列的練習:
-
#斐波那契數列 1 1 2 3 5 8 13 ··········· def fun(num): if num == 1: return 1 elif num == 2: return 1 else: return fun(num-1)+fun(num-2) a = 6 print("斐波那契數列第",a,"位的數值為:",fun(a)) #斐波那契數列第 6 位的數值為: 8 #輸出斐波那契數列每一項的值 for x in range(1,a+1): print(fun(x),end=" ") #1 1 2 3 5 8
-
8.總結:
補充:
- 拆包
- 在調用擁有多值參數的函數時,如果希望元組傳遞給*args,字典傳遞給**kwargs,就可以使用拆包
- 在調用時,給實參加上分別加上*和**
-
a = (1,2,3,4,5) b = {"name":"zms","age":35} fun(a,b) #運行結果 ((1, 2, 3, 4, 5), {'name': 'zms', 'age': 35}) {} #拆包 fun(*a,**b) #運行結果 (1, 2, 3, 4, 5) {'name': 'zms', 'age': 35}
- 匿名函數:
- 語法格式:
函數名 = lambda [參數列表]:表達式
-
代碼演示:
1 # 無參數匿名函數 2 import random 3 4 sum = lambda :print("python") 5 sum() #python 6 7 random_lambda = lambda : random.randint(1,3) 8 print( random_lambda() ) # 1 2 3 9 10 # 匿名函數 11 sum = lambda args1,args2=10: args1+args2 12 print(sum(20)) # 30 13 print(sum(20,30)) # 50 14 15 # 利用三元表達式 16 get_odd_even = lambda x: "%s是偶數"%x if x%2==0 else "%s是奇數"%x 17 print(get_odd_even(8)) #8是偶數 18 print(get_odd_even(9)) #9是奇數 19 20 # map函數 與 函數結合 21 def add(x): 22 return x**2 23 mobj = map(add, [1,2,3,4]) 24 print( list(mobj) ) # [1, 4, 9, 16] 25 #使用 匿名函數 與 map函數 結合 26 mobj_1 = map(lambda x: x**2, [1,2,3,4,5]) 27 print( list(mobj_1) ) #[1, 4, 9, 16, 25]
- 語法格式: