函式
一、函式的基本使用
1.函式簡介
使用函式目的就是為了減少重復撰寫代碼
回圈:在相同的地方反復執行代碼
函式:在不同的地方反復執行代碼
沒有函式:維修工每次作業的時候都要先創造工具再作業
有函式:維修工再作業的時候直接拿工具過來作業
2.函式的語法結構
定義階段
def 函式名(形參):
'''函式注釋'''
函式體代碼
return 回傳值
呼叫階段
函式名(實參)
1.def
? 定義函式的關鍵字
2.函式名
? 命名等同于變數名 要做到見名知意
3.形參
? 可以寫 (單個或多個)也可以不寫
? 主要用于接受外界傳遞給函式體代碼內部的資料
? 可以理解為使用該函式的條件
4.函式體注釋
? 類似于說明說 介紹函式的功能和使用方法
5.函式體代碼
? 整個函式的核心 寫邏輯代碼的地方
6.return
? 使用完函式后有沒有相應的反饋
3.函式的的定義與呼叫
函式名()執行優先級最高(定義階段除外)
- 函式必須先定義在呼叫
- 函式在定義階段只檢測函式體代碼的語法 不執行函式體代碼
- 函式在呼叫階段才會執行函式體代碼
- 定義函式用def 呼叫函式用函式名()
4.函式的分類
-
內置函式
解釋器直接定義好的可以直接用的 eg:len() #資料型別的內置方法也是算內置函式 必須要用.的方式才能使用 -
自定義函式
-
空函式
函式體代碼為空 用pass或...補全 空函式主要作為前期專案搭建 def func(): pass -
無參函式
函式定義階段括號內沒有填寫引數 def func(): print('xxx') #無參函式直接函式名加括號呼叫即可使用 -
有參函式
函式名定義階段括號里寫引數 def func(a): print(a) func(123) #123 #有參函式需要函式名加括號并傳對應個數的實參才能呼叫
-
5.函式的回傳值
-
什么是回傳值
呼叫函式之后回傳給呼叫者的結果 也可以理解為函式體代碼執行完有沒有反饋
-
如何獲取回傳值
變數名 = 函式名()
def func(): a = 666 print(123) return a res = func() print(res) #666 123 ''' 函式定義階段 不執行函式體代碼 看同一級別 res=func() 函式名加括號呼叫 執行函式體代碼 a = 666 列印123 return 回傳666 結束函式體代碼 把結果系結給res 列印res ''' -
函式回傳值的多種情況
-
函式體代碼中沒有return關鍵字 :默認回傳None
def func(): pass res=func() print(res) #結果為:None -
函式體代碼有return關鍵字:后面不寫東西也回傳None
def func(): pass return res=func() print(res) #結果為:None -
函式體代碼有return關鍵字:后面有什么就回傳什么(變數名就回傳對應的資料值)
def func(): pass return 123 #如果是變數名則回傳對應的值 res=func() print(res) #結果為:123 -
函式體代碼有return關鍵字:后面多個資料之用逗號隔開 默認自動組織成元組回傳 串列需要自己定義
def func(): pass return 1,2 #多個資料值用逗號隔開 res=func() print(res) #結果為:(1,2) ''' 串列字典需自己定義 return [1,2] 如果有一個沒定義則外層用元組包起來([1,2],3) ''' -
當函式體代碼遇到return關鍵字會立刻結束函式體代碼的運行(類似于break)
def func(): print('上面') return 'a' print('下面') #永遠不會執行 func() #結果為:上面
-
二、函式的引數
1.形式引數與實際引數
-
形式引數
函式在定義階段括號內填寫的引數 def func(a): pass -
實際引數
函式在呼叫階段括號內填寫的引數 func(1) -
形參與實參的關系
1.形參類似于變數名 實參類似于資料值 def func(name): pass func('jason') #此時jason與name臨時系結 2.函式在呼叫階段 形參與實參會動態系結 函式體代碼運行結束會立刻解除系結 def func(name): print(name) func(1) func(2) #第一次1與name系結 函式體代碼結束 解除系結 name與2系結
1.位置形參 與位置實參
位置形參:函式在定義階段括號內從左往右依次填寫的變數名
位置實參:函式在呼叫階段括號內從左往右依次填寫的資料值
def func(a,b,c):# 位置形參
pass
func(1,2,3)# 位置實參
'''
1.實參可以是資料值,也可以是系結了資料值的變數名,
2.位置實參在給位置形參傳值的時候個數必須一致,不能多也不能少會報錯,
'''
2.關鍵字實參與默認值形參(關鍵字形參)
關鍵字實參:函式在呼叫階段括號內以什么等于什么傳值(name=‘jason’)
def func(a,b):
print(a,b)
func(a=10,b=20) # 關鍵字實參 也可以:(b=20,a=10)
func(10,b=20) # 位置實參與關鍵字實參可以一起使用
'''
1.關鍵字實參給位置形參傳值時打破了位置的限制,不用按照從左到右依次賦值,
2.需注意當位置實參和關鍵字實參一起用時,位置實參要在最后,
【簡單在前,復雜在后,同復雜隨便,但是需滿足第三點】
3.呼叫函式時,一個形參只能接收一個實參
eg:
func(10,a=10) 此時由于位置形參a已經等于10了,關鍵字實參又給a賦了20的值,所以報錯
'''
默認值形參:也可以叫做關鍵字形參 函式在定義階段括號內以什么等于什么填寫引數
用了默認值形參 用戶傳值就用用戶傳的值 用戶不傳就用默認的值
'默認值形參的定義也遵循【簡單在前,復雜在后,同復雜隨便】,但是一個形參只能接收一個實參'
def register(name,gender='男'): # 默認值形參
print(name,gender)
1) register('jason') # 第二個可以不傳實參
#結果為:jason 男
2) register('jason','女') # 第二個可以傳位置實參
#結果為:jason 女
3) register('jason',gender='女') # 第二個可以傳關鍵字實參
#結果為:jason 女
————————————————————————————————————————————
def register(gender='男'): # 默認值形參
print(gender)
register() # 不傳實參
#結果為:男
3.默認長形參(*與**在形參中的作用)
*在形參中:接收多余的位置引數 并整理成元組賦值給后面的變數名>> * args
def func(*args):
print(args)
func() # () 沒有引數給args所以是空元組
func(1) # (1,)
func(1,2,3) # (1,2,3)
——————————————————————————————————
def func(a,*args):
print(a,args)
func() # 結果會報錯 因為a需要傳一個引數
func(1) # 1 () 1賦值給a,沒有引數給args所以是空元組
func(1,2,3) # 1 (2,3) 1賦值給a,2,3賦值給args組成元組
**在形參中:接收多余的關鍵字引數并組成字典賦值給后面的>> * * kargs
def func(**kwargs):
print(kwargs)
func() # {} 沒有關鍵字實參給kwargs所以是空字典
func(a=1) # {'a':1}
func(a=1,b=2) # {'a':1,'b':2}
——————————————————————————————————
def func(a,**kwargs):
print(a,kwargs)
func() # 結果會報錯 因為a需要傳一個引數
func(1) # 1 {}
func(1,b=22) # 1 {'b':22}
*與 * *結合使用:無論怎么傳值都可以執行
def func(*args,**kwargs):
print(args,kwargs)
func() # () {}
func(1,a=2) # (1,) {'a':2}
'位置實參給*結果是元組'
'關鍵字實參給**結果是字典'
4.可變長形參(* 與 ** 在實參中的作用)
*在實參中:相當于把串列、字典、字串、元組、集合利用for回圈取出一次性傳給函式
def func(a,b):
print(a,b)
l1=[1,2]
s1='zz'
d1={'a':1,'b':2} #字典在做回圈時只有鍵參與
func(*l1) # 結果為:1 2
func(*s1) # 結果為:z z
func(*d1) # 結果為:a b
'當形參中沒有*與**時,結果就是一個一個的資料值'
————————————————————————————————————
def func(*args,**kwargs):
print(args,kwargs)
l1=[1,2]
s1='zz'
d1={'a':1,'b':2} #字典在做回圈時只有鍵參與
func(*l1) # 結果為:(1, 2) {}
func(*s1) # 結果為:('z', 'z') {}
func(*d1) # 結果為:('a', 'b') {}
'當形參中有*與**時,結果*就是元組,**就是字典'
** 在實參中:僅針對字典 把字典的鍵值對當作關鍵字實參 一次性傳給函式
def func(**kwargs):
print(kwargs)
d={'name':'jason','age':18}
func(**d) # 結果為:{'name': 'jason', 'age': 18}
'**d就等同于 name="jason" age=18'
5.命名關鍵字引數
當要求形參必須使用關鍵字實參傳值
def func(*args,c,**kwargs):#注意c的位置要在**kwargs前
print(args,c,kwargs)
func(1,2,c=3,a=4,b=5)
#結果為:(1,2) 3 {'a':4,'b':5}
'1和2給*args,關鍵字實參c=3給形參c,多余的關鍵字實參給**kwargs'
三、名稱空間
1.名稱空間
名稱空間就是用來存放變數名與資料值系結關系的地方 也可以理解為是存盤變數名的地方
name = '張三'
#底層原理:
1.在記憶體中申請一塊空間存盤'張三'
2.給'張三'系結一個變數名name
3.此時變數名與資料值之間的系結關系就會存放在名稱空間中
4.后續使用變數名name就可以找到'張三'
'del 變數名 其實就是清除變數名與資料值的系結關系'
名稱空間的分類
-
內置名稱空間
python解釋器運行時立刻創建的名稱空間(里面存放內置的名字>>len()input()等
-
全域名稱空間
py檔案運行代碼的程序中產生的名字就會儲存在改空間中>>普通代碼的變數名、分支回圈的變數名、函式名、類名
-
區域名稱空間
函式體代碼/類體代碼 執行程序中內部產生的名字都會被存放在該空間中
2.名稱空間存戶哦周期及作用范圍(作用域)
名稱空間存活周期:
-
內置名稱空間
? 創建:解釋器運行 銷毀:解釋器結束
-
全域名稱空間
? 創建:py檔案運行 銷毀:py檔案結束
-
區域名稱空間
? 創建:函式體/類體代碼運行 銷毀:函式體/類體代碼結束
作用域:
-
內置名稱空間
程式的任何位置都可以使用
-
全域名稱空間
程式的任何位置都可以使用
-
區域名稱空間
只能在區域名稱空間使用 且不互通
3.名字的查找順序
a = '全域'
def func():
a = '區域'
func()
print(a)
#結果是:全域
——————————————————————————————————————————————
#查找名字時要先確定自己在哪個名稱空間
1.當前在區域名稱空間時
區域名稱空間 >> 全域名稱空間 >> 內置名稱空間
2.當前在全域名稱空間時
全域名稱空間 >> 內置名稱空間
4.區域名稱空間案例
-
相互獨立的區域名稱空間默認不能互相訪問
def func1(): name='jason' print(age) def func2(): age=18 print(name) #此時呼叫以上兩個函式會報錯,因為區域名稱空間不互通 -
區域名稱空間嵌套
def func1(): #1 x = 2 #3 def func2(): #4 x=3 #6 def func3(): #7 x=4 #9 print(x) #10 func3() #8 func2() #5 func1() #2 結果為4 當函式嵌套呼叫時,一直打開到最內層的func3,則print(x)會從最內層往外查找 哪個最先有x就用哪個 '這里需要注意:當func3里的x=4如果在print(x)下面 就會報錯,因為函式在定義階段名字的查找順序就已經固定好了,它知道要在func3里查找 但是由于x=4還沒定義出來 所以會報錯,除非把func3中的x=4去掉才會往外一層找x=3'
5.global與nonlocal
了解:修改不可變型別(整型、浮點、字串、元組)需用關鍵字宣告
修改可變型別(串列、字典、集合)不需要關鍵字宣告
不過經過實驗可變型別加上關鍵字也不影響!所以不用糾結什么型別,都加上關鍵字也問題不大!!
- 區域修改全域的資料 golabal
【不可變型別】:
a = 10
def func():
global a # 區域修改全域資料
a = 99 # 把全域a=99
func()
print(a) # 結果為:99
————————————————————————————————
【可變型別】:
a = [1,2,3]
def func():
a[0] = 99 # 把全域a串列索引0改為99
func()
print(a) # 結果為:[99,2,3]
-
內層區域修改外層區域的資料(函式嵌套)nonlocal
【不可變型別】: def outer(): a = 10 def inner(): nonlocal a # 內層區域改外層區域資料 a = 99 # 把外層區域a=99 inner() print(a) # 結果為99 outer() ———————————————————————————————— 【可變型別】: def outer(): a = [1,2,3] def inner(): a[0] = 99 # 把外層區域a串列索引0改為99 inner() print(a) # 結果為:[99,2,3] outer()
四、裝飾器
1.函式名的多種用法
列印函式名發現:函式名其實系結的就是一個記憶體地址 該地址里存放著一段代碼 函式名加括號就會找到該代碼然后去執行
-
可以當作變數名多次賦值
def func(): pass a=b=func # 讓a和b同時系結func a() # 此時a加括號可以呼叫func函式 b() # b加括號也可以呼叫func函式 -
可以當作函式的引數
def func(): print('我是另外一個函式func') def func1(a): a() # 2.傳進來的func加括號就可以呼叫func函式 func1(func) # 1.把func函式名當作引數傳給func1 #結果為:我是另外一個函式func -
可以當作函式的回傳值
def index(): print('index') def func(): print('func') return index # 回傳值也可以寫一個函式名 res=func() # 變數名res接收func的回傳值 接收的就是index函式名 print(res) # 列印res就是index的記憶體地址 res() # res()就等同于index() -
可以當作容器型別的資料(串列、字典、元組、集合)
def func(): pass l1 = [1,2,func] # 函式名可以放在串列中當作資料值 l1[-1]() # 串列取值加括號也可以呼叫函式 ———————————————————————————————————— def func(): print(111) d1 = {'1':func} # 函式名可以放在字典中當作值 d1.get('1')() # 字典取值加括號也可以呼叫函式 -
編程套路
#編程套路 def register(): print('注冊功能') def login(): print('登錄功能') dict={ '1':register, '2':login, } while True: print(""" 1.注冊 2.登錄 """) choice=input('輸入指令:').strip() if choice in dict: dict.get(choice)() else: print('指令不存在')
2.閉包函式
閉包函式:定義在函式內部的函式(函式嵌套) 且內部函式用到外部函式名稱空間中的名字
閉包函式的作用:提供了另一種給函式傳參的方式
#給函式體傳參的方式:
方式一:直接傳參
def register(name):
print(name)
register('jason')
#結果為:jason
方式二:閉包函式
def outer(name):
def inner():
print(name) # 內部函式用外部函式的名字name
return inner
res=outer('jason') # 給外部函式傳一個值jason并把回傳值(內部函式名)賦給res
res() # res()等同于inner()
#結果為:jason
3.裝飾器簡介
-
裝飾器本質
在不改變被裝飾物件原來的呼叫方式和內部代碼下 給被裝飾物件添加新的功能
-
裝飾器原則
不許修改 只許擴展
-
儲備知識 time模塊
#時間相關操作 import time #匯入一個時間模塊 print(time.time())# 1665482833.111403 距離1970年1月1日0時0分0秒所經歷的秒數 time.sleep(3) # 讓程式原地等待3秒 _____________________________________________ import time count = 0 start_time = time.time() # 回圈前獲取一下時間為開始時間 while count < 1000: print(123) count += 1 end_time = time.time() # 回圈后獲取一下時間為結束時間 print('執行時間:', end_time - start_time) #結束時間-開始時間=共用時多久
4.裝飾器推導流程
要求:1.再不改變被裝飾物件原代碼和呼叫方式的情況下給被裝飾物件添加新的功能
? 2.統計index函式的執行時間
import time
def index():
time.sleep(3)
print('這是index函式')
def func():
time.sleep(1)
print('這是func函式')
'''1.直接在呼叫index函式的前后添加代碼'''
start_time=time.time()
index()
end_time=time.time()
print('函式的執行時間:',end_time-start_time)
#缺陷:當index呼叫的地方較多時,反復拷貝代碼太麻煩
'''2.解決:相同的代碼在不同地方反復執行,用函式包起來'''
def get_time():
start_time = time.time()
index()
end_time = time.time()
print('函式的執行時間:', end_time - start_time)
get_time()
#缺陷1.函式體代碼寫死了,只能統計index函式的執行時間
#缺陷2.改變了原代碼的呼叫方式
'''3.解決缺陷1:利用傳參來讓統計的函式寫活'''
def get_time(xx):
start_time = time.time()
xx()
end_time = time.time()
print('函式的執行時間:', end_time - start_time)
get_time(func) #要統計哪個函式就把哪個函式當作引數傳進來
get_time(index) #要統計哪個函式就把哪個函式當作引數傳進來
'''4.解決缺陷2:直接傳參不行就用閉包函式傳參'''
def outer(xx):
def get_time():
start_time = time.time()
xx()
end_time = time.time()
print('函式的執行時間:', end_time - start_time)
return get_time
res=outer(index) #要統計哪個函式就把哪個函式當作引數傳進來
res()
res=outer(func) #要統計哪個函式就把哪個函式當作引數傳進來
res()
#缺陷:改變了原代碼的呼叫方式
'''5.解決:把接識訓傳值的變數名寫死,寫成index'''
def outer(xx):
def get_time():
start_time = time.time()
xx()
end_time = time.time()
print('函式的執行時間:', end_time - start_time)
return get_time
index=outer(index) #要統計哪個函式就把哪個函式當作引數傳進來
index()
#缺陷:只可以用無參函式,如果是有參函式會報錯
'''6.解決:給函式體內部加變數名去接收引數'''
def func1(a):
time.sleep(2)
print('這是func1函式')
def outer(xx): #xx就是func1
def get_time(a):
start_time = time.time()
xx(a)
end_time = time.time()
print('函式的執行時間:', end_time - start_time)
return get_time
index=outer(func1) #index就是get_time
index(1) #index()就是get_time()
#缺陷:有參函式如果是多個引數也不兼容
'''7.解決:接收引數的變數名用可變長引數代替'''
def func1(a):
time.sleep(2)
print('這是func1函式')
def func2(a,b,c,d):
time.sleep(2)
print('這是func2函式')
return 123
def outer(xx): #xx就是func1
def get_time(*args,**kwargs):
start_time = time.time()
xx(*args,**kwargs)
end_time = time.time()
print('函式的執行時間:', end_time - start_time)
return get_time
index=outer(func2) #index就是get_time
index(1,2,3,4) #index()就是get_time()
res=index(1,2,3,4)
print(res)#結果是None 因為現在的index其實是get_time!
#缺陷:如果被裝飾的函式有回傳值則會回傳一個None
'''8.找到真正要執行函式的位置讓一個res去接收他的回傳值(xx就是真正執行的)'''
def func1(a):
time.sleep(2)
print('這是func1函式')
def func2(a,b,c,d):
time.sleep(2)
print('這是func2函式')
return 123
def outer(xx): #xx就是func1
def get_time(*args,**kwargs):
start_time = time.time()
res=xx(*args,**kwargs)
end_time = time.time()
print('函式的執行時間:', end_time - start_time)
return res
return get_time
index=outer(func2) #index就是get_time
res=index(1,2,3,4)
print(res)#結果就是對應函式的回傳值!

5.裝飾器模板
def func1():
print('func1')
return 111
def func2(a):
print('func2')
return 222
——————————————————————————————————————————————————————
#裝飾器模板:
def outer(func): #func用來系結真正被裝飾的物件記憶體地址
def inner(*args,**kwargs):
#執行被裝飾物件之前做的額外操作
res = func(*args,**kwargs)
#執行被裝飾物件之后做的額外操作
return res
return inner
————————————————————————————————————————————————————————
#呼叫:
【無參】
func1=outer(func1) #左邊func1就是inner 右邊的func1就是把真正函式名當引數傳給裝飾器
res=func1() #func1()就是inner() 并接收inner的回傳值 inner的回傳值就是真的函式的回傳值
print(res) #列印真正被裝飾的函式的回傳值
【有參】
func2=outer(func2)
res=func2(1)
print(res)
6.裝飾器語法糖
語法糖會自動將下面緊挨著的函式名當作第一個引數自動穿給@函式呼叫
【定義】
def outer(func_name):
def inner(*args, **kwargs):
print('執行被裝飾物件之前可以做的額外操作')
res = func_name(*args, **kwargs)
print('執行被裝飾物件之后可以做的額外操作')
return res
return inner
_________________________________
@outer # 等同于呼叫階段的func=outer(func)
def func():
print('這是func函式')
return '111func'
@outer # 等同于呼叫階段的index=outer(index)
def index():
print('這是index函式')
return '111index'
_________________________________
【呼叫】
#func = outer(func) 加了語法糖則不需要該操作
func()
#index=outer(index) 加了語法糖則不需要該操作
index()
7.多層語法糖
def outter1(func1): #func1=wrapper2
print('加載了outter1')
def wrapper1(*args, **kwargs):
print('執行了wrapper1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outter2(func2): #func2=wrapper3
print('加載了outter2')
def wrapper2(*args, **kwargs):
print('執行了wrapper2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outter3(func3): # func3=真正的index
print('加載了outter3')
def wrapper3(*args, **kwargs):
print('執行了wrapper3')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
@outter1 # index=outer1(wrapper2)>>回傳值為wrapper1
@outter2 # outer2(wrapper3)>>回傳值為wrapper2
@outter3 # outer3(index)>>回傳值為wrapper3
def index():
print('from index')
index()
'''
運行結果:
加載了outter3
加載了outter2
加載了outter1
執行了wrapper1
執行了wrapper2
執行了wrapper3
from index
'''
注意
1.多層語法糖的順序是從下往上的
2.語法糖h會自動將他緊挨著的函式名當作引數傳給@的函式呼叫
3.多次語法糖在每次執行后如果上面還有語法糖則直接將回傳的資料傳給上面的語法糖
? 如果上面沒有語法糖后變形為index = outer1(wrapper2)

8.有參裝飾器
當裝飾器中需要額外的引數的時候就要用到有參裝飾器
'函式名加括號執行優先級最高'
有參裝飾器流程:
@outer('1')
1.先看函式名加括號的執行 outer('1')
2.再看語法糖的操作 @outer
# 校驗用戶是否登錄裝飾器
def outer(mode): #mode='1'
def login_auth(func):
def inner(*args,**kwargs):
username=input('username>>:').strip()
password=input('password>>:').strip()
if mode=='1':
print('資料來源寫死')
elif mode=='2':
print('資料來源于文本檔案')
elif mode=='3':
print('資料來源于字典')
#res=func(*args,**kwargs) #此處不寫則不會執行真正index里的print
#return res #此處不寫則不會執行真正index里的print
return inner
return login_auth
@outer('1') #變形為語法糖后就是@login_auth,語法糖把最近的函式名變成引數傳給outer:index=login_auth(真正的index)
def index():
print('from index')
index()
@outer('2')
def func():
print('from func')
func()

9.有參裝飾器與無參裝飾器模板
無參:
def outer(func):
def inner(*args,**kargs):
#執行裝飾器之前做的操作
res = func(*args,**kargs)
#執行裝飾器之后做的操作
return res
return inner
@outde
def index():
pass
有參:
def oouter(mode)
def outer(func):
def inner(*args,**kargs):
#執行裝飾器之前做的操作
res = func(*args,**kargs)
#執行裝飾器之后做的操作
return res
return inner
return outer
@outde(1)
def index():
pass
10.裝飾器修復技術
help(函式名)
#補充知識:
def func():
"""這是index函式"""
pass
help(func) #會告訴這個名字的基本資訊
#結果為:func()
# 這是index函式
讓裝飾器看起來更逼真:
from functools import wraps # 匯入一個wraps模塊
def outer(func_name):
@wraps(func_name) # 僅僅是為了讓裝飾器更逼真(用戶用help方法也看不出來)
def inner(*args, **kwargs):
"""我是inner 我擅長讓人蒙蔽"""
res = func_name(*args, **kwargs)
return res
return inner
@outer
def func():
"""我是真正的func 我很強大 我很牛 我很聰明"""
pass
help(func)#help查看該函式真正資訊就會發現是真正的func函式資訊
'如果不加wraps模塊查看的就是inner函式資訊'
print(func)#<function func at 0x000002DB3568EE50>
五、遞回函式
1.遞回函式
使用遞回函式注意:
- 函式直接或者間接呼叫自己就叫遞回呼叫
- 每次呼叫都必須比上一次簡單 且要有一個明確的結束條件
遞回函式的應用場景:
-
遞推:一層一層往下尋找答案
-
回溯:根據明確的條件往上得出結果
1.遞回——直接呼叫:('自己調自己') def index():#1 print('這是index函式')#3 index()#4 index()#2 #執行順序:12343434一直重復 —————————————————————————————————————————— 2.遞回——間接呼叫:('別人調我,我調別人') def index():#1 print('這是index函式')#6 func()#7 def func():#2 print('這是func函式')#4 index()#5 func()#3 #執行順序:123456745674567一直重復pycharm允許函式最大呼叫的次數是1000 實際略有偏差
count = 0 def index(): print('這是index') global count count + = 1 print(count) index() index() #結果為:會一直執行到996或997
練習:
問A年齡,A說我比B大2歲
問B年齡,B說我比C大2歲
問C年齡,c說我比D大2歲
問D年齡,D說我比E大2歲
問E年齡,E說我18歲
"""
get_age(5) = get_age(4) + 2
get_age(4) = get_age(3) + 2
get_age(3) = get_age(2) + 2
get_age(2) = get_age(1) + 2
get_age(1) = 18
"""
def get_age(n):
if n == 1:
return 18
return get_age(n-1) + 2
res = get_age(5)
print(res)
六、演算法、三元運算式、各種生成式
演算法:解決問題的有效方法
應用場景:推薦演算法(視頻推送)、成像演算法(線上試衣)
常見演算法:二分法、冒泡、快排、插入...等等
1.二分法
二分法:不斷地對資料進行切割分成兩份進行判斷 只要找到張耀的結果
要求:待查找的資料必須是有序的
缺陷:因為是從中間開始切割 開頭或者結尾查找效率低
#eg:用演算法二分法判斷82在不在串列中,如果在則取出
l1=[11,23,35,43,55,62,75,82,94]
def get_num(l1,num):
#添加一個結束條件
if len(l1)==0:
print('沒找到')
return
#1.獲取串列中間資料的索引值
middle=len(l1)//2 # 整除:要除完后的整數 4
#2.比較目標82與中間數4的大小
if num>l1[middle]: # 82>l1[4]
#切片保留右半邊串列
right_l1=l1[middle+1:] # 索引4的55比較過 所以切索引4+1
print(right_l1)#[62,75,82,94]
return get_num(right_l1,num)
elif num<l1[middle]:
# 切片保留左半邊串列
left_l1=l1[:middle] # 切片索引取值顧頭不顧尾 所以索引4默認切到索引3
print(left_l1)#[11, 23, 35, 43]
return get_num(left_l1,num)
else:
print(f'找到了')
get_num(l1,82)
get_num(l1,20)
"""
將想要查找的串列和資料傳到函式里
對串列的長度進行整除
判斷 想要查找的資料 和整除得出的中間數是否相等
大于 就往右切[middle:]middle要加1 因為顧頭不顧尾 中間數已經比過一次了 回傳切好的串列和查找資料 接著切 直到找到位置
小于就往左切[:middle]
"""
2.三元運算式
作用:主要是用來精簡代碼
場景:當二選一的時候使用三元大運算式比較簡單 但是不建議多個三元運算式嵌套
- 如果name是張三則列印''你好'',否則列印''你誰啊'
#普通代碼:
name='張三'
if name=='張三':
print('你好')
else:
print('你誰啊')
——————————————————————————————————
#使用了三元運算式:
name='張三'
res='你好'if name=='張三'else '你誰啊'
print(res)
-
函式比較兩個數的大小,回傳較大的數字
#普通代碼: def max_func(a,b): if a>b: return a else: return b res=max_func(1,10) print(res) —————————————————————————————— #使用了三元運算式: def max(a,b): return a if a>b else b res=max(1,10) print(res)
3.各種生成式(運算式/推導式)
-
串列生成式
#語法結構: 【簡單】: new_list=[資料操作 for i in 串列變數名] 【復雜】: new_list=[資料操作 for i in 串列變數名 if 條件] """ 串列生成式只能出現for和if,不能后面再加else,因為系統識別不出來else屬于誰 執行流程: 1.先執行for回圈把一個一個資料值給if判斷 2.if判斷完直接把資料值交給前面進行資料操作然后組成一個新串列 """#給l1所有值后面加一個你好 l1=['a','b','c'] 【普通代碼】: l2=[] for i in l1: l2.append(i+'你好') print(l2) # ['a你好', 'b你好', 'c你好'] —————————————————————————————————————————— 【簡單串列生成式】: l2=[i+'你好' for i in l1] print(l2) # ['a你好', 'b你好', 'c你好'] 'for回圈把一個一個資料值給前面進行資料操作 然后組成一個新串列' ——————————————————————————————————————————— 【復雜串列生成式】: l1=['a','b','c'] l2=[i+'你好' for i in l1 if i=='a'] print(l2) # ['a你好'] 'for回圈把一個一個資料值給后面的if判斷條件,把滿足該條件的資料給前面進行資料操作 然后組成一個新串列' -
字典生成式
#補充知識: enumerate() #for回圈一個串列時還會同時產生一個默認從0開始的編號(括號里加start=1則是從1開始) s1='a,b,c' for i,j in enumerate(s1,start=1): print(i,j) #結果為: 1 a # 2 b # 3 cs1='abc' d1={i:j for i,j in enumerate(s1)} print(d1) #{0:'a',1:'b',2:'c'} """for回圈s1字串,并呼叫一個enumerate函式,編號賦值給i,字串里單個值賦值給j, 然后再給到資料操作上組成i:j""" -
集合生成式
s1='abbbbcc' res={i for i in s1} print(res) #{'b','c','a'} """for回圈s1字串 并把一個一個字符給前面的資料操作上組成集合(集合自動去重且無序)"""
沒有元組生成式 元組該操作后面叫迭代器/生成器
七、匿名函式
匿名函式就是沒有名字的函式 要用關鍵字lambda
#語法結構:
lambda 形參:要回傳的值
#使用場景:(主要用在簡單的邏輯功能上)
一般都是配合'內置函式一起使用',主要用來減少代碼
#eg:用函式統計a+b的結果
【普通代碼:】
def func(a,b):
return a+b
res=func(1,10)
print(res)#結果為:11
——————————————————————————————————————
【匿名函式:】#只是為了了解匿名函式怎么回事!并不是真正使用方法
res=lambda a,b:a+b
print(res)#<function <lambda> at 0x000001C91F375280>
a=res(1,10)
print(a)#結果為:11
"""這樣做就是給匿名函式變成了有名函式,不符合匿名函式的應用場景!,此處只做簡單介紹,真正要配合下面內置函式一起使用!!!"""
八、重要的內置函式
1.map()映射
把一個東西經過函式的操作變成另一個東西 或稱就叫做映射
#eg:讓串列每一個資料值加1
"""不使用匿名函式"""
l1=[1,2,3]
def func(a):
return a+1
res=map(func,l1)#map()括號里填寫一個函式,和可迭代物件,回傳值讓res接收
print(list(res))#map也類似于一個工廠,你找他要才會給你,所以要用一個list括起來列印
#結果為:[2,3,4]
——————————————————————————————————
"""使用匿名函式"""
l1=[1,2,3]
res=map(lambda a:a+1,l1) #map(lambda 形參:回傳值,可迭代物件)
print(list(res))#結果為:[2,3,4]
#結果為:[2,3,4]
以上可以看出用匿名函式就是為了簡化一下需要簡單操作的函式代碼
2.max() 求最大 min() 求最小
==默認括號內不用傳函式 如果是字典要傳函式比較v值 則在括號內寫key=func 會自動加括號呼叫 比大小
【串列操作】
#取串列中最大的資料值
l1=[1,2,3,4]
res=max(l1)#底層也是用for回圈取每一個資料值判斷最大值
print(res)
【字典操作】
"""錯誤做法"""
#字典在做for回圈的時候只是在回圈鍵!!鍵在比較大小時會用ASCII碼來比較!
d1 = {
'zj': 100,
'jason': 8888,
'berk': 99999999,
'oscar': 1}
res=max(d1)
print(res) #錯誤做法!這里比較的是鍵的ascii碼大小
————————————————————————————————————————————————————
"""不使用匿名函式"""
def func(a):
return d1.get(a)
res=max(d1,key=func) #for回圈字典的每一個鍵當作引數執行func函式,回傳值為字典V值然后做比較
print(res)
#結果為:break 回傳的結果是K鍵(比較用V值比較,回傳K鍵)
————————————————————————————————————————————————————
"""使用匿名函式"""
res=max(d1,key=lambda a:d1.get(a))#max(key=lambda 形參:回傳值,可迭代物件)
print(res)
#結果為:break 回傳的結果是K鍵(比較用V值比較,回傳K鍵)
以上可以看出用匿名函式就是為了簡化一下需要簡單操作的函式代碼
3.reduce()傳多個值經過操作回傳一個值
#語法結構:
reduce(函式,可迭代物件)
#用reduce時注意python3解釋器把它移到了模塊里,需要先匯入模塊
"""使用匿名函式操作"""
from functools import reduce #需匯入模塊
l1=[1,2,3]
res=reduce(lambda a,b:a+b,l1)#reduce(lambda 形參1,形參2:回傳值,可迭代物件)
print(res)
#結果為:6
for回圈可迭代物件,'讓a和b接收串列的前兩個值'然后回傳(a+b的結果),再回圈依次讓加過給a,取串列下一個值給b然后回傳(a+b的結果)
4.zip() 拼接多個資料集并組成元組
把多個資料集用for回圈依次取出 然后分別組成員組 也需要list括起來
木桶原理 資料集個數不同時按照最短的來拼接
eg:把以下資料集中的資料拼接為一個串列
l1 = [1,2,3,4]
s1 = 'abc'
t1 = (1,2,3,4)
res = zip(l1, s1, t1)
print(list(res))
#結果為:[(1,'a',1), (2,'b',2), (3,'c',3)]
——————————————————————————————————————————————————
還可以把以上資料解壓賦值
l1 = [1, 2, 3, 4]
s1 = 'abc'
t1 = (1, 2, 3, 4)
a,b,c= zip(l1, s1, t1)
print(a,b,c)
#結果為:(1,'a',1) (2,'b',2) (3,'c',3)
5.filter()過濾篩選
#語法結構:
filter(函式,可迭代物件)
#底層原理是for回圈可迭代物件然后當作引數給函式去處理后回傳滿足的結果
eg:過濾取出串列里大于2的資料
l1 = [5,2,3,4]
"""不使用匿名函式操作"""
def index(a):
return a>2
res = filter(index,l1)
print(list(res))
#結果為:[5,3,4]
——————————————————————————————
"""使用匿名函式操作"""
res = filter(lambda a:a>2,l1)#filter(lambda 形參:回傳值,可迭代物件)
print(list(res))
#結果為:[5,3,4]
6.sorted()排序
#語法結構
sorted(可迭代物件) #默認升序
'括號內加reverse=True 則為降序'
#給串列升序排序:
l1=[1,8,3,6]
res=sorted(l1)
print(res)
#結果為:[1,3,6,8]
————————————————————————————
#給串列降序排序:
l1=[1,8,3,6]
res=sorted(l1,reverse=True)
print(res)#[8, 6, 3, 1]
九、常見內置函式
1.abs()
把資料值變成絕對值(正數)
print(abs(10)) #10
print(abs(-10))#10
2.all()
判斷容器型別中所有資料值對應的布林值都是True結果就是True 否則就是False 類似于and
print(all([1,2,3])) # True
print(all([0,1,2])) # False
3.any()
判斷容器型別中所有資料值對應的布林值有一個結果就是True 類似于or
print(any([0,None,''])) # False
print(any([1,0,None,''])) # True
4.bin()oct()hex()int()
print(bin(10)) # 十 >> 二
print(oct(10)) # 十 >> 八
print(hex(10)) # 十 >> 十六
print(int(0b1010)) # 二 >> 十
print(int(0o12)) # 八 >> 十
print(int(0xa)) # 十六 >> 十
5.bytes()
類似于編碼解碼
s1='哈'
res=s1.encode('utf8') # 編碼
print(res) # b'\xe5\x93\x88'
res1=res.decode() # 解碼
print(res1) # 哈
res=bytes(s1,'utf8') # 轉為bytes型別用utf8
print(res) #結果為:b'\xe5\x93\x88'
res1=str(res,'utf8') # 轉為字串型別用utf8
print(res1) #結果為:哈
6.callable()
判斷一個名字是否可以加括號呼叫
name = 'jason'
def func():
pass
print(callable(name)) # False
print(callable(func)) # True
7.chr()ord()
根據ASCII碼表左數字語字母轉換
print(chr(65))#A
print(ord('A'))#65
8.dir()
獲取物件內部可以通過句點符呼叫的方法名字
print(dir(name))
9.divnod()
獲取出完后的整數與余數
i,j=divmod(10,2)
print(i,j)
#結果為:5 0
10.enumerate()列舉
for回圈可迭代物件時還會同時產生一個默認從0開始的編號(括號內加start=1 則是從1開始)
s1='abc'
for i,j in enumerate(l1,start=1)
print(i,j)
#結果為:1 a
# 2 b
# 3 c
11.eval()exec()
可以識別字串中的python代碼并執行
區別:eval只能識別簡單的
? exec可以識別復雜的
s1 = 'print(123)'
eval(s1) # 只能識別簡單的結構
#結果為:123
——————————————————————————————————————
s2='for i in range(10):print(i)'
exec(s2) # 能識別復雜的結構
#結果為:回圈列印1~9
12.hash()
加密 回傳的是密文
print(hash('123'))
#結果為:5474369475686684015
13.id()input()isinstance()
id() # 獲取物件的記憶體地址
s1='aaa'
print(id(s1)) # 2996384812336
input() # 接收用戶輸入的資訊
choice = input('輸入指令:').strip()
isinstance() # 判斷某個資料型別是否屬于某個資料型別
print(isinstance(123,int)) # True
print(isinstance(123,str)) # False
14.open()
檔案操作:打開一個檔案
with open(r'a.txt','r',encoding='utf8')as f:
print(f.read())
#結果為:檔案里的內容
15.pow()
求冪指數
print(pow(2,3)) # 8
print(pow(2,4)) # 16
16.range()
類似于工廠生產資料 要什么給什么
for i in range(10):
print(i)
#結果為:列印0~9的數字
17.round()
五舍六入(python對數字不敏感)
print(round(10.5)) # 10
print(round(10.6)) # 11
18.sum()
求和
l1=[1,2,3]
print(sum(l1)) # 6
十、可迭代物件
可迭代物件在python中可以理解為能否被for回圈
1.可迭代物件:
? 物件內置方法里面有__iter__方法的就是可迭代物件
2.迭代的含義:
? 就是更新換代 每次更新都基于上一次的結果
3.可迭代物件的范圍
? 可迭代物件:str、list、dict、set、tuple、檔案物件
? 不可迭代物件:int、float、bool、函式物件
十一、迭代器物件
1.迭代器物件:
? 可迭代物件呼叫__iter__方法后 就是迭代器物件
? 只要內置方法里有__iter__和__next__方法的都是
? __next__方法就是迭代取值 for回圈的本質
2.簡寫:
? __iter__簡寫:iter()
? __next__簡寫:next()
3.迭代器物件的作用:
? 提供了一種不依賴于索引取值的方式
? 迭代器物件可以讓字典、集合這些無序的型別能夠被for回圈
4.注意事項:
? 可迭代物件呼叫完__iter__后會變成迭代器物件
? 迭代器物件如果在呼叫__iter__則不會有任何變化 還是迭代器物件本身
5.迭代器物件實操:
-
不用for回圈依次列印出字串的單個字符
s1 = 'abc' # 字串是可迭代物件 res = s1.__iter__() # 可迭代物件呼叫雙下iter方法后變成迭代器物件 print(res.__next__()) # 結果未:a print(res.__next__()) # 結果為:b print(res.__next__()) # 結果未:c print(res.__next__()) # 當沒有值可獲取了會報錯 -
不用for回圈一次發引出串列所有值
l1 = [1, 2, 3] # 串列是可迭代物件 count = 0 res = l1.__iter__() # 可迭代物件呼叫雙下iter方法后變成迭代器物件 while count < 3: print(res.__next__()) # 迭代取值 count += 1
6.for回圈內部原理
#語法結構:
for 變數名 in 可迭代物件:
回圈體代碼
#底層邏輯:
"""
1.for回圈會先將可迭代物件呼叫__iter__方法變成迭代器物件
2.然后依次讓迭代器物件呼叫__next__方法取值
3.一但__next__取不到值后報錯,但是for回圈會【自動捕獲并處理】
"""
十二、例外捕獲/例外處理
1.例外
? 代碼在運行中的報錯就是例外 也叫bug
2.例外的分類
語法錯誤:不允許出現
邏輯錯誤:允許出現
3.例外結構

一般最后一條報錯資訊就是真正的錯誤位置
4.例外的常見型別
SyntaxError: 語法錯誤
NameError: 名稱錯誤
IndexError: 索引錯誤
KeyError: 鍵錯誤
TypeError: 型別錯誤
IndentationError: 縮進錯誤
......
5.例外的多種語法結構
-
基本語法結構
錯誤型別后可跟
as e:列印e就是系統提示的錯誤原因#基本語法結構 try: 可能會出錯的代碼(待檢測的代碼) except 錯誤型別: 針對上述錯誤型別指定的方案 —————————————————————————————————————— try: name except NameError: print('名字沒被定義就使用') #結果為:當例外型別為名稱錯誤時會執行下面的列印,其他型別的錯誤會報錯#升級版語法結構(可把系統提示的例外原因列印出來) try: 可能會出錯的代碼(待檢測的代碼) except 錯誤型別 as e: 針對上述錯誤型別指定的方案 print(e) #可把系統提示的例外原因列印出來 —————————————————————————————————————— try: name except NameError: print('名字沒被定義就使用') print(e)#name 'name' is not defined -
給不同的錯誤型別制定不同的解決方案
try: 可能會出錯的代碼 except 錯誤型別1 as e: 針對錯誤型別1的解決措施 except 錯誤型別2 as e: 針對錯誤型別2的解決措施 ... -
萬能例外
任何錯誤資訊都可以使用該方案
try: 待檢測的代碼 except Exception as e: 處理方案 -
結合else使用
當檢測的代碼中沒有報錯則執行else子代碼
try: 可能會出錯的代碼 except Exception as e: 針對所有錯誤型別統一的處理方案 else: 當以上沒有報錯時執行的子代碼 -
結合finally使用
檢測的代碼無論報不報錯最后都要走finally子代碼
try: 可能會出錯的代碼 except Exception as e: 針對所有錯誤型別統一的處理方案 finally: 無論以上代碼是否報錯都會執行的子代碼 -
斷言
預測接受的資料是什么型別 不對就報錯 對就正常執行
name='張三' assert isinstance(name,str) #預測name是不是字串,對則往下執行,不對就報錯 print('猜對咯') -
主動報錯
主動報錯 當沒錯誤的時候報錯 有錯誤才正常走
name='張三' if name=='張三': raise Exception('一樣的?報錯!不走了!') else: print('不一樣?好小子我喜歡,走我')
6.例外處理練習題
注意:例外處理能少用就少用 只有出現無法控制的情況才應該考慮使用
1.用while回圈+例外處理+迭代器物件 完成for回圈迭代取值的功能,
l1=[1,2,3,4,5]
res=l1.__iter__()
while True:
try: #例外捕獲
print(res.__next__())
except Exception as e: #當報錯時結束
break
十三、生成器物件
1.生成器物件
本質還是迭代器物件
只不過生成器是 自己定義 出來的 迭代器物件是 解釋器自動提供的
2.生成器物件語法結構
-
函式體代碼中有yield關鍵字 那么函式名加括號不會呼叫函式體代碼 而是產生了一個生成器物件 (也叫迭代器物件)用變數名接收 可以理解為呼叫了__iter__
def my_iter(): print('嘿嘿嘿') yield res=my_iter() #res內置方法中有__iter__、__next__方法,所以res就是生成器物件 -
列印該變數名出來的是生成器物件 只有用該變數名點__next__()方法才會迭代取值
def my_iter(): print('嘿嘿嘿') yield res=my_iter() print(res) # <generator object my_iter at 0x000001E5D260C890> res.__next__() #結果為:嘿嘿嘿 -
每次執行完__next__() 方法后代碼會停在yield位置 等下次執行該方法時才會繼續下一個值
def my_iter(): print('嘿嘿嘿') yield print('哈哈哈') yield res=my_iter() res.__next__() # 結果為:嘿嘿嘿 res.__next__() # 結果為:哈哈哈 -
yield類似于回傳值
def my_iter(): yield 123 res=my_iter() print(res.__next__()) #結果為:123 ______________________________________ def my_iter(): print('嘿嘿') yield 123 res=my_iter() print(res.__next__()) #結果為:嘿嘿 和 123各一行 -
yield冷門用法:用yield去接收資料
#send(資料值) 將資料值傳給yield前面的變數名,然后會自動呼叫__next__方法 def eat(name,food=None): print(f'{name}正在吃東西') while True: food=yield print(f'{name}正在吃{food}') res=eat('張三') res.__next__() #結果為:張三正在吃東西 res.send('包子') #結果為:張三正在吃包子
3.生成器物件練習題
1.自定義生成器對標range功能(一個引數 兩個引數 三個引數 迭代器物件)
def my_range(start_num,end_num=None,step=1):
# 傳3個引數時就把+=1的1改為step,不傳值默認未1,傳幾則間隔幾
# 傳一個引數時:
if not end_num:
end_num=start_num
start_num=0
# 傳兩個引數時:
while start_num<end_num:
yield start_num
start_num+=step
# for i in my_range(1,10,2):
# print(i)
for i in range(100,50,-1):
print(i)
4.生成器運算式
生成器運算式就是生成器的簡化寫法!主要就是為了節省記憶體
#串列生成式:
l1=[i for i in range(5)]
print(l1) #結果為:[0,1,2,3,4]
#生成器運算式(沒有元組生成式)
l1=(i for i in range(5))
print(l1) #結果為:<generator object <genexpr> at 0x000002CECB2BE120>
#print(l1.__next__()) #結果為:0
#print(l1.__next__()) #結果為:1 需列印呼叫多次才可以取完
for i in l1:
print(i) #結果為:把0~4回圈列印出來
5.生成器面試題
def add(n, i): # 1.普通函式 回傳兩個數的和 求和函式
return n + i
def test(): # 3.生成器
for i in range(4): #i為 0 1 2 3
yield i
g = test() # 2.激活生成器 g的值為 0 1 2 3
for n in [1, 10]:
g = (add(n, i) for i in g)
"""
第一次for回圈
g = (add(n, i) for i in g) #此時的n為1
第二次for回圈
g = (add(n, i) for i in (add(n, i) for i in g)) #此時的n為10
""" #10+0 10+0
res = list(g)
print(res)
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23] √
#D. res=[21,22,23,24]
'''不用深入研究 大致知道起始數即可'''
十四、索引取值與迭代取值的差異
| 索引取值 | 迭代取值 | |
|---|---|---|
| 優點 | 可以任意位置任意次數取值 | 支持所有型別的資料取值(無序、有序) |
| 缺點 | 不支持無序型別的資料值(字典、集合) | 只能從前往后依次取值 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/542311.html
標籤:Python
下一篇:學習筆記——SpringMVC訊息轉換器概述;使用訊息轉換器處理請求報文;使用訊息轉換器處理回應報文;使用訊息轉換器處理Json格式資料
