函數
Outline
·1.作用
·2.使用步驟
·3.參數
·4.傳回值
·5.說明文檔
·6.函數嵌套
·7.變量作用域
·8.多函數程式執行流程
·9.傳回值進階
·10.參數進階
·11.拆包和交換兩個變量的值
·12.引用
·13.可變與不可變類型
·14.遞歸
·15.lambda表達式
·16.高階函數
1. 函數的作用
·函數是将 一段具有獨立功能的代碼塊整合到一個整體并命名,在需要的位置 調用這個名稱即可完成對應的需求
·函數在開發過程中,可以高效地實作 代碼重用
2. 函數的使用步驟
2.1 定義函數
·文法:
def 函數名(參數):
代碼1
代碼2
......
2.2 調用參數
·文法:
·注意:
1.對于不同的需求,參數可有可無
2.在Python中函數必須 先定義後使用
·實作功能時可以學習的地方:先搭建整體架構(複現需求),再補充需求内容
print('密碼正确登入成功')
# 顯示"選擇功能"界面
print('查詢餘額完畢')
# 顯示"選擇功能"界面
print('取了2000元錢')
# 顯示"選擇功能"界面
# 需求:複現ATM取錢功能
def select_fun():
# 功能:查詢餘額、存款、取款
print("--------------------")
print("請選擇要辦理的業務")
print("查詢餘額")
print("存款")
print("取款")
print("--------------------")
print("密碼正确!登入成功!")
select_fun()
print("餘額查詢完畢")
select_fun()
print("取了2000元")
密碼正确!登入成功!
--------------------
請選擇要辦理的業務
查詢餘額
存款
取款
--------------------
餘額查詢完畢
--------------------
請選擇要辦理的業務
查詢餘額
存款
取款
--------------------
取了2000元
3. 函數的參數
·思考:若定義了如下函數完成1+2地運算,如果想要這個函數變得更靈活,可以計算任何使用者指定的兩個數字的和,如何書寫程式?
# 定義函數
def add_num1():
result = 1 + 2
print(result)
# 調用函數
add_num1()
·分析:使用者要在調用函數的時候指定具體數字,那麼在定義函數的時候就需要接收使用者指定的數字。函數調用時候指定的數字和定義函數時候接收的數字即是函數的參數。
# 實作任意兩數相加
def add_2num(a,b):
print(f"{a} + {b} = {a+b}")
add_2num(99,1)
99 + 1 = 100
3.1 參數傳遞
·在python中,類型是屬于對象的,而變量是沒有類型的:
a = [1,2,3]
a = 'lcj'
·在以上代碼中,[1,2,3]是一個list類型,'lcj’是一個string類型,而變量a是沒有類型的,他僅僅隻是一個對象的引用(可以了解為一個指針),可以是指向list類型對象,也可以是指向string類型對象
3.1.1 可變類型對象及其參數傳遞
在python中,list、dict、set等是可以修改的對象。
·變量指派 la=[1,2,3,4] 後再指派 la[2]=5 則是将 list la 的第三個元素值更改,本身la沒有動,隻是其内部的一部分值被修改了。
參數傳遞:
·類似 C++ 的引用傳遞,如 清單,字典。如 fun(la),則是将 la 真正的傳過去,修改後 fun 外部的 la 也會受影響
3.1.2 不可變類型對象及其參數傳遞
在python中,string、tuple、numbers是不可變資料類型
·變量指派 a=5 後再指派 a=10,這裡實際是新生成一個 int 值對象 10,再讓 a 指向它,而 5 被丢棄,不是改變 a 的值,相當于新生成了 a。
參數傳遞:
·類似 C++ 的值傳遞,如整數、字元串、元組。如 fun(a),傳遞的隻是 a 的值,沒有影響 a 對象本身。如果在 fun(a) 内部修改 a 的值,則是新生成一個 a 的對象。
# 不可變類型對象參數傳遞
def change(a):
print(id(a)) # 指向的是同一個對象
a=10
print(id(a)) # 一個新對象
a=1
print(id(a))
change(a)
# 可變類型對象參數傳遞
# 可寫函數說明
def changeme( mylist ):
"修改傳入的清單"
mylist.append([1,2,3,4])
print ("函數内取值: ", mylist)
return
# 調用changeme函數
mylist = [10,20,30]
changeme( mylist )
print ("函數外取值: ", mylist)
140713493473056
140713493473056
140713493473344
函數内取值: [10, 20, 30, [1, 2, 3, 4]]
函數外取值: [10, 20, 30, [1, 2, 3, 4]]
4. 函數的傳回值
·在函數中,如果需要傳回結果給使用者,則需要使用函數傳回值。
# 計算任意兩數之和,并儲存結果
def sum_num(a,b):
return a + b
result = sum_num(10,99)
print(result)
109
5. 函數的說明文檔
·文法:
def 函數名(參數):
""" 說明文檔的位置 """
代碼
......
·檢視函數的說明文檔
help(函數名)
def test_fun():
"""
本函數用來測試函數說明文檔作用
換行
"""
help(test_fun)
Help on function test_fun in module __main__:
test_fun()
本函數用來測試函數說明文檔作用
換行
6.函數的嵌套調用
·函數的嵌套調用是指一個函數中又調用了另個函數
def testA():
print('---- testA start ----')
print("這是函數A執行的代碼塊")
print('---- testA done ----')
def testB():
print('---- testB start ----')
testA()
print('---- testB done ----')
testB()
---- testB start ----
---- testA start ----
這是函數A執行的代碼塊
---- testA done ----
---- testB done ----
7. 變量作用域
·變量作用域指的是變量生效的範圍。主要分為兩類:局部變量和全局變量
7.1 局部變量
·定義:局部變量是定義在函數體内部的變量,即隻在函數體内部生效
·作用:在函數體内部,臨時儲存資料,即當函數調用完成後,銷毀局部變量
# 局部變量
def test1():
A = 100
print(A)
test1()
print(A) # 會報錯
100
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-3bd390f0e5e9> in <module>
5
6 test1()
----> 7 print(A) # 會報錯
NameError: name 'A' is not defined
7.2 全局變量
·全局變量是指在函數體内、外都可以生效的變量
# 定義全局變量
B = 100
def test2():
print(B) # 通路全局變量B,并列印其存儲的資料
def test3():
print(B) # 通路全局變量B,并列印其存儲的資料
test2()
test3()
100
100
·思考:如果test3需要修改變量B為200,應該如何修改?
# 修改全局變量--1
B = 100
listx = [10,20,30]
def test2():
print(B) # 通路全局變量B,并列印其存儲的資料
def test3():
B = 200
listx.append([1,2,3])
print(B) # 通路全局變量B,并列印其存儲的資料
test2()
test3()
print(f"全局變量B的值:{B}")
print(listx)
100
200
全局變量B的值:100
[10, 20, 30, [1, 2, 3]]
我們發現這樣修改全局變量其實并沒有生效,最後列印出來的全局變量B的值仍然為100;
這裡我們添加一個清單作為對照,可以很清楚的看到,清單在函數中的修改中是生效了的,這就是我們上文提到的可變與不可變資料類型的差別,作為指向不可變資料對象的B變量,在test3中試圖修改時,其實是函數在内部建立了一個名字也叫B的局部變量;而作為可變資料類型的清單,則是将清單對象直接傳入test3函數中修改。
最後,我們如何在函數體内修改全局變量呢?
→使用global關鍵字
# 修改全局變量--2
B = 100
def test2():
print(B) # 通路全局變量B,并列印其存儲的資料
def test3():
# 使用global關鍵字聲明B是全局變量
global B
B = 200
print(B) # 通路全局變量B,并列印其存儲的資料
test2()
test3()
print(f"全局變量B的值:{B}")
100
200
全局變量B的值:200
8. 多函數程式執行流程
·在實際開發過程中,一個程式往往由多個函數組成,并且多個函數可能會共享某些資料
8.1 共用全局變量
# 定義全局變量
glo_num = 100
def test1():
global glo_num
glo_num = 1 # 修改全局變量
def test2():
print(glo_num) # 調用全局變量
test2() # 列印修改前的全局變量
test1() # 修改全局變量
test2() # 列印修改後的全局變量
100
1
8.2 傳回值作為參數傳遞
def test1():
return 99
def test2(num):
print(num)
result = test1() # 儲存test1的傳回值
test2(result) # 将傳回值作為參數傳遞到test2中
99
9. 傳回值進階
·思考:如果一個函數有多個return語句,執行結果會怎樣?
Answer:隻會執行第一個,因為return會退出目前函數,導緻第一個return後面的代碼不會再執行
·如何傳回多個值?
# 多個傳回值函數
def return_2():
return 1,2,3
result = return_2()
print(result)
print(type(result))
(1, 2, 3)
<class 'tuple'>
注意:
·
return a,b
寫法在傳回多個資料的時候,預設為元組類型
·return後面可以連接配接清單、元組或字典,以傳回多個值
10. 參數進階
10.1 位置參數
·定義:調用函數時根據函數定義的參數位置來傳參
·注意:傳遞和定義參數的順序及個數必須一緻!
# 位置參數
def fun_location(name,age,gender):
print(f"Your name : {name}\nYour age : {age}\nYour gender : {gender}")
fun_location('lee',20,'male')
Your name : lee
Your age : 20
Your gender : male
10.2 關鍵字參數
·通過“key=value”形式加以指定。使用關鍵字參數允許函數調用時參數的順序與聲明時不一緻,因為 Python 解釋器能夠用參數名比對參數值。
·注意:函數調用時,如果有位置參數時,位置參數必須在關鍵字參數的前面,但關鍵字參數之間不存在先後順序。
# 關鍵字參數
def fun_keyword(name,age,gender):
print(f"Your name : {name}\nYour age : {age}\nYour gender : {gender}")
fun_keyword('Lee',age=20,gender='male')
fun_keyword(age=21,gender='male',name='jcl')
Your name : Lee
Your age : 20
Your gender : male
Your name : jcl
Your age : 21
Your gender : male
10.3 預設參數
·也叫預設參數,用于定義函數,為參數提供預設值,調用函數時可不傳該預設參數的值
·注意:
1.所有位置參數必須出現在預設參數之前,包括函數定義和調用
2.函數調用時,如果為預設參數傳值則修改預設參數值,否則使用預設值
# 預設參數
def fun_tolerant(name,age,gender='男'):
print(f"Your name : {name}\nYour age : {age}\nYour gender : {gender}")
fun_tolerant('TOM',21)
fun_tolerant('Rose',19,'female')
Your name : TOM
Your age : 21
Your gender : 男
Your name : Rose
Your age : 19
Your gender : female
10.4 不定長參數
·也叫可變參數。用于不确定調用的時候會傳遞多少個參數(也可以不傳參)的場景。此時可用包裹(packing)位置參數,或者包裹關鍵字參數,來進行參數傳遞。
·加了星号 * 的參數會以元組(tuple)的形式導入,存放所有未命名的變量參數。
# 包裹位置傳遞
def user_info(*args):
print(args)
user_info('Tom')
user_info('Tom',18,'male')
('Tom',)
('Tom', 18, 'male')
·注意:傳進的所有參數都會被args變量收集,它會根據傳進參數的位置合并為一個元組(tuple),args是元組類型,這就是包裹位置傳遞。
# 包裹關鍵字傳遞
def user_info(**kvargs):
print(kvargs)
user_info(name='lee',age=18,gender='male')
{'name': 'lee', 'age': 18, 'gender': 'male'}
11. 拆包和交換變量值
11.1 拆包
# 拆包:元組
def return_num():
return 1,99
num1,num2 = return_num()
print("元組拆包")
print(num1)
print(num2)
print('-'*10)
# 拆包:字典
dict1 = {'name':'lee','age':20}
a,b = dict1
print("字典拆包") # 字典拆包,取出來的是字典的key值
print(a)
print(b)
print(dict1[a])
print(dict1[b])
元組拆包
1
99
----------
字典拆包
name
age
lee
20
11.2 交換變量值
·需求:變量
a = 10
,
b = 20
,交換兩個變量的值
# 正常方法:借助中間變量實作交換
a,b = 10,20
c = 0 # 定義中間變量
c = a # 将a的值儲存到c
a = b # 将b的值賦給a
b = c # 将a的值賦給b
print(f"a的值:{a}\nb的值:{b}")
a的值:20
b的值:10
# 友善方法!
a, b = 1, 2
print(f"交換前:\na的值:{a}\nb的值:{b}")
a, b = b, a
print(f"交換後:\na的值:{a}\nb的值:{b}")
交換前:
a的值:1
b的值:2
交換後:
a的值:2
b的值:1
12. 引用
12.1 了解引用
·python中,值是靠引用來傳遞的
我們可以用id()來判斷兩個變量是否為同一個值的引用
id可以了解為那塊記憶體的位址辨別
# 1. 不可變資料類型:以int為例
a = 1
b = a
print(b)
print(f"a的位址:{id(a)}\nb的位址:{id(b)}")
a = 2
print(b) # 說明int為不可變資料類型
print(f"a的位址:{id(a)}\nb的位址:{id(b)}")
1
a的位址:140713493473056
b的位址:140713493473056
1
a的位址:140713493473088
b的位址:140713493473056
# 2. 可變資料類型:以list為例
aa = [10,20]
bb = aa
print(bb)
print(f"aa的位址:{id(aa)}\nbb的位址:{id(bb)}")
aa.append(99)
print(bb) # bb的值也變化,說明清單為可變資料類型
print(f"aa的位址:{id(aa)}\nbb的位址:{id(bb)}")
[10, 20]
aa的位址:1658821373440
bb的位址:1658821373440
[10, 20, 99]
aa的位址:1658821373440
bb的位址:1658821373440
12.2 引用當作實參
def test1(a):
print(f"a值為:{a}\na位址為:{id(a)}")
a += a
print(f"a值為:{a}\na的位址:{id(a)}")
# int
b = 100
test1(b)
# list
c = [11,22]
test1(c)
a值為:100
a位址為:140713493476224
a值為:200
a的位址:140713493479424
a值為:[11, 22]
a位址為:1658825582336
a值為:[11, 22, 11, 22]
a的位址:1658825582336
13. 可變與不可變資料類型
·可變資料類型:清單、字典、集合
·不可變資料類型:Numbers、字元串、元組
14. 遞歸
14.1 應用場景
·遞歸是一種程式設計思想:
1.在我們日常開發中,如果要周遊一個檔案夾下面所有的檔案,通常會使用遞歸來實作;
2.在後續的算法課程中,很多算法都離不開遞歸,例如:快速排序。
·特點:
1.函數内部自己調用自己
2.必須有出口
# 遞歸應用:累加
def sum_num(num):
# 判斷:如果為1則直接傳回1-----遞歸的出口
if num == 1:
return 1
# 如果不是1,則遞歸求解
return num + sum_num(num-1)
result = sum_num(10) # 1-10累加
print(result)
55
15. lambda表達式
·也叫匿名函數:
所謂匿名,意即不再使用 def 語句這樣标準的形式定義一個函數。
lambda 隻是一個表達式,函數體比 def 簡單很多。
lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
lambda 函數擁有自己的命名空間,且不能通路自己參數清單之外或全局命名空間裡的參數。
雖然lambda函數看起來隻能寫一行,卻不等同于C或C++的内聯函數,後者的目的是調用小函數時不占用棧記憶體進而增加運作效率。
·應用場景:如果一個函數有一個傳回值,且隻有一句代碼,則可以使用lambda表達式簡化。
·文法:
lambda 參數清單 : 表達式
·注意事項:
1.lambda表達式的參數可有可無,函數的參數在lambda表達式中完全适用。
2.lambda表達式能接收任何數量的參數但隻能傳回一個表達式的值。
3.直接列印lambda表達式,輸出的是此lambda的記憶體位址
# lambda表達式計算a+b
fn = lambda a, b: a + b
print(fn)
print(fn(1,2))
<function <lambda> at 0x00000182399A00D0>
3
# lambda的參數形式
# 無參
fn1 = lambda:100
print(fn1())
# 一個參數
fn2 = lambda a: a
print(fn2("Hello,World!"))
# 預設參數
fn3 = lambda a, b, c=100: a + b + c
print(fn3(5,3))
# 可變參數:*args
fn4 = lambda *args: args
print(fn4(1,2,3))
# 可變參數:**kvargs
fn5 = lambda **kvargs: kvargs
print(fn5(name='lee',age=20))
100
Hello,World!
108
(1, 2, 3)
{'name': 'lee', 'age': 20}
·注意:可變參數*args傳入lambda後傳回值為元組
# lambda表達式應用1---帶判斷的lambda
fn1 = lambda a, b: a if a > b else b
print(fn1(40,99))
99
# 資料按字典key的值排序
students = [
{'name': 'TOM', 'age': 20},
{'name': 'ROSE', 'age': 19},
{'name': 'Jack', 'age': 22}
]
# 按name升序排列
students.sort(key=lambda x: x['name'])
print(students)
# 按name降序排列
students.sort(key=lambda x: x['name'],reverse=True)
print(students)
# 按age值升值排序
students.sort(key=lambda x: x['age'])
print(students)
[{'name': 'Jack', 'age': 22}, {'name': 'ROSE', 'age': 19}, {'name': 'TOM', 'age': 20}]
[{'name': 'TOM', 'age': 20}, {'name': 'ROSE', 'age': 19}, {'name': 'Jack', 'age': 22}]
[{'name': 'ROSE', 'age': 19}, {'name': 'TOM', 'age': 20}, {'name': 'Jack', 'age': 22}]
16. 高階函數
·把函數作為參數傳入,這樣的函數稱為高階函數,高階函數是函數式程式設計的展現。函數式程式設計就是指這種高度抽象的程式設計範式。
"""
abs():求數字的絕對值
round():對數字四舍五入
需求:任意兩個數字,按照指定要求整理數字後求和
"""
# 方法1.
def add_num(a,b):
return abs(a)+abs(b)
result = add_num(-1,9)
print(result)
# 方法2.
def sum_num(a,b,f):
return f(a)+f(b)
result2 = sum_num(-1,9,abs)
print(result2)
result3 = sum_num(1.2,9.4,round)
print(result3)
10
10
10
16.1 内置高階函數
16.1.1 map()
·map(func, lst),将傳入的函數變量func作用到lst變量的每個元素中,并将結果組成新的疊代器(Python3)傳回。
# 需求:計算list序列中各個數字的2次方
list1 = [1,2,3,4,5]
func = lambda x: x**2
result = map(func,list1)
print(result)
print(list(result))
<map object at 0x000001823A18C280>
[1, 4, 9, 16, 25]
16.1.2 reduce()
·reduce(func,lst),其中func必須有兩個參數。每次func計算的結果繼續和序列的下一個元素做累積計算。
·注意:reduce()傳入的參數func必須接收兩個參數
# 需求:計算list1序列中各個數字的累加和
import functools
list1 = [1,2,3,4,5]
func = lambda a, b: a + b
result = functools.reduce(func,list1)
print(result)
15
16.1.3 filter()
·filter(func, lst)函數用于過濾序列, 過濾掉不符合條件的元素, 傳回一個 filter 對象。如果要轉換為清單, 可以使用 list() 來轉換。
# filter()
list1 = [1,2,3,4,5,6,7,8,9,10]
func = lambda x: x % 2 == 0
result = filter(func,list1)
print(result)
print(list(result))
<filter object at 0x00000182395B3340>
[2, 4, 6, 8, 10]