一、迭代器
1、什麼是迭代
1.1 迭代是一個重複的過程,每一次重複都是基於上一次的結果而進行的。
1.2 單純的重複並不是迭代
2、爲什麼要用迭代器
1、列表,元組,字符串都可以依靠索引把值取出來,而一些數據類型沒有索引,那麼如何取出裏面的值呢? --這裏就要用到迭代器!
2、迭代器就是一種可以不依賴索引的迭代取值方式!
二、迭代器分爲:可迭代對象和不可迭代對象
2.1、可迭代的對象(數據類型)如下: 在python中,但凡帶內置有__iter__()方法的數據類型(或者對象),都是可迭代的對象!
1.列表類型
2.元組類型
3.集合類型
4.字典類型
5.文本類型(文本類型本身就是迭代器對象"具有__next__()"方法)
2.2、不可迭代的對象(數據類型)如下:
1.整數類型
2.浮點數類型
二、迭代器對象 (通過調用可迭代對象拿到迭代器對象)
2.1 如何拿到一個迭代器對象?
執行可迭代對象下的__iter__()方法,得到的返回值就是一個迭代器對象!
c=['a','b'] #列表類型
res=c.__iter__()
print(res)
>>:<list_iterator object at 0x0000017A993592E8>
三、使用迭代器對象 迭代器對象有,內置的__next__()方法 ,同時具有__iter__()方法 (執行該方法得到的仍然是迭代器本身)
3.1、使用迭代器對象的__next__()方法,就可以取出列表內的值,這就是通過迭代器取值。(不依賴索引取值!!)
例子1:使用迭代器取出字典裏面的key和value
dic={'aaa':123,'bbb':456}
res_dic=dic.__iter__()
迭代獲取字典裏面的key
print(next(res_dic)) #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第1個key)
print(next(res_dic)) #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第2個key)
aaa
bbb
迭代獲取字典裏面的value
print(dic[next(res_dic)]) #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第1個value)
print(dic[next(res_dic)]) #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第2個value)
123
456
例子2:使用迭代器取出文件裏面的內容
f=open('a.txt','rt',encoding='utf-8')
iter_obj=f.__iter__()
print(iter_obj.__next__(),end='') #end='' 表示設置換行符爲空
print(iter_obj.__next__(),end='') #end='' 表示設置換行符爲空
print(iter_obj.__next__(),end='') #end='' 表示設置換行符爲空
print(iter_obj.__next__(),end='') #end='' 表示設置換行符爲空
>>:111
>>:22222
>>:33333333
>>:44444444444
3.2、 當字典內的值被取光了,就會報錯"StopIteration" (這是一個結束信號,表示迭代器的值被取完了),如何解決?
1、使用try+except StopIteration的方式
dic={'k1':1,'k2':1,'k3':1}
res=dic.__iter__()
while True:
try:
print(res.__next__()) #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第1個)
except StopIteration:
break
k1
k2
k3
四、總結迭代器對象
1、可迭代對象不一定是迭代器對象!
1.列表類型 2.元組類型 3.集合類型 4.字典類型 都不具備__next__()方法。
2、迭代器對象一定是可迭代對象!
1.文本類型(文本類型本身就是迭代器對象"具有__next__()"方法)具備__iter__()方法,同時也具備__next__()方法。
3、總結迭代器的優缺點
缺點:1、只能往後取值,不能往前取值,是一次性的,值取乾淨後無法再次取值,除非重新得到新的迭代器對象。
優點:1、提供了一種不依賴於索引的迭代取值方式 2、節省內存
五、for 循環的底層運行機制 for循環也叫做迭代器循環
5.1、能被for循環,循環的對象,它一定是可迭代對象!
dic={'k1':1,'k2':1,'k3':1}
for k in dic:
print(k)
for循環的機制:
1、先調用in後那個對象的__iter__()方法,得到該對象的迭代器對象: ### iter_obj=dic.__iter__()
2、執行迭代器對象的__next__()方法,將得到的返回值賦值給in前面的變量名,然後執行一次循環體代碼: ### iter_obj.__next__()
3、循環往復,直到把dic裏面的值全部取完,然後自動捕捉"StopIteration"這個異常,結束循環。
六、生成器 (學習生成器是爲了掌握一種自定義迭代器的方式)
6.1、什麼是生成器 ?
1、在函數內但凡有yield關鍵字,在調用函數時就不會執行函數體內的任何代碼,得到的返回值就是一個生成器對象[一串內存地址](生成器的本質就是迭代器)。
2、生成器本身就是迭代器。
def func():
print('11111')
yield 1
print('22222')
print('33333')
res=func()
print(res)
>>:<generator object func at 0x00000204A8F51B48>
6.2、使用生成器 -- 用yield來控制返回值(yield和函數內的return很相似),取到想要的值
nxet(res)過程:
會觸發生成器res所對應的函數體代碼的運行,直到遇到yield關鍵字,把yield後的返回值當做本次next操作的結果返回
def func():
print('11111')
yield 1 # 函數體代碼運行到此處時會暫停,把yield之前運行的代碼以及yield後面的1作爲返回值返回
print('22222')
yield 2 # 函數體代碼運行到此處時會暫停,把yield之前運行的代碼以及yield後面的2作爲返回值返回
print('33333')
res=func()
print(next(res)) # 執行next(res)獲取到的值爲"11111",然後繼續執行代碼遇到了yield,那麼就把yield後面的1作爲本次結果的返回值
11111
1
print(next(res)) # 執行next(res)獲取到的值爲"22222",然後繼續執行代碼遇到了yield,那麼就把yield後面的2作爲本次結果的返回值
22222
2
print(next(res))
33333
StopIteration # 由於函數內的"print('33333')"這行代碼之後無yield關鍵字,那麼next(res)在取到'33333'這個值之後,沒有其他值可以取了,就會拋出一個StopIteration異常。
for n in res:
print(n)
11111 # 這是執行函數體代碼的結果
1 # 這是for循環變量n的值
22222 # 這是執行函數體代碼的結果
2 # 這是for循環變量n的值
33333 # 這是執行函數體代碼的結果,由於後面並沒有yield關鍵字,就會拋出一個StopIteration異常。
過程解析:
執行for循環相當於執行了:res.__next__() == next(res):
1、 首先next(res)會執行遇到yield之前的代碼print('11111'),然後拿到"yield 1"把1最爲本次結果的返回值賦值給n,然後打印n,那麼第一次循環結束。
2、然後執行next(res),執行函數內"yield 1"之後的代碼,直至遇到下一個"yield"關鍵字。
小例子:自定義函數模擬range(1,10,2)
def my_range(start,stop,step=1):
print('開始')
while start < stop:
yield start
start+=step
print('結束')
for n in my_range(1,10,2):
print(n)
開始
1
3
5
7
9
結束
6.3、生成器如何運行?
1、首先要明白生成器本身就是迭代器,那麼運行生成器就可以按照迭代器的方式去運行!
2、運行生成器就會觸發迭代器所對應的代碼,並開始運行迭代器的代碼。
3、運行迭代器的代碼時,只要碰到yield,代碼就會停止運行,並拿到yield 的返回值。
6.4、yield關鍵字的另外一種使用形式:表達式形式的yield應用
1、先讓函數運行起來,然後暫停到某一處,然後傳一個值運行一次代碼,傳一個值運行一次代碼。
通過:生成器.send的方法給yield 傳值,例子如下:[執行g.send就相當於給yield傳值,同時執行一次next(g)]
def dog(dog_name):
print('狗準備開吃!')
food_list=[]
while True:
print(1)
food=yield food_list # food=yield='骨頭'
print('狗:%s,吃了%s' %(dog_name,food))
food_list.append(food)
print(2)
g=dog('doudou') #拿到一個迭代器
next(g) #next(g)==g.send(None),開始運行函數體代碼,next(res)==res.__next__()
#強調:對於表達式形式的yield的生成器,在使用前必須先用next(g)或者g.send(None)初始化一次。
res2=g.send('骨頭')
print(res2)
res3=g.send('骨頭1')
print(res3)
狗準備開吃
1
[]
dog: doudou 吃了 骨頭
2
1
['骨頭']
dog: doudou 吃了 骨頭1
2
1
['骨頭', '骨頭1']
註釋:
1、yield後面的 food_list 在沒有沒傳值的情況下,默認爲None。
2、執行一次g.send('骨頭')後,會把'骨頭'傳值給 yield ,並執行一次"yield"關鍵字之後的函數體代碼,直到遇見下一個“yield”爲止。
3、如上例子,當g.send('骨頭')後,循環一遍結束,直到遇見一個新的yield爲止。此時 food_list 的值就爲None(默認值)。
七、面向過程編程
1、什麼是面向過程編程?
核心是過程二字,過程指的是解決問題的步驟,即先幹什麼,在幹什麼,在幹什麼。。。。
基於面向過程的思想,編寫程序就好比在設計一條流水線,是一種機械式思維方式。
優點:將複雜的問題流程化,進而簡單化。
缺點:修改了某一個階段,其他相關聯的階段都有可能受到影響,牽一髮而動全身,擴展性極差。
應用:應用於對擴展性要求不高的場景。
面向過程編程小例子:把要做的事情拆分爲一件件小的事情,然後完成。
def login():
uname=input('pls input uname>>:').strip()
pwd=input('pls input pwd>>:').strip()
return uname,pwd
def auth(uname,pwd):
if uname == 'szq' and pwd == '123':
return True,uname
else:
return False,uname
def index():
if res[0]:
print('歡迎%s登錄' %res[1])
else:
print('%s登錄失敗' % res[1])
uname,pwd=login()
res=auth(uname,pwd)
index()
八、三元表達式
x=10
y=30
res=x if x >y else y #如果x的值大於y的值,那麼返回x,否則返回y的值。
print(res)
30
九、列表生成式,字典生成式,生成器表達式
當數據量不多的時候,用列表表達式
1、列表生成式 -- 直接將產生的結果放到列表裏面
list=[n for n in range(10)] # 每一次for循環取到的值,都會賦值給給左邊的n,然後放在列表list裏面,然後接着下一次循環
print(list)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for 循環後接if判斷
list=[n for n in range(10) if n > 3] # 每一次for循環取到的值(然後執行右邊的if判斷,符合要求的值),都會賦值給給左邊的n,然後放在列表list裏面,然後接着下一次循環
print(list)
[4, 5, 6, 7, 8, 9]
例子1:將小寫全部改爲大寫
names=['aaa','bbb','ccc','ddd']
names=[name.upper() for name in names]
print(names)
['AAA', 'BBB', 'CCC', 'DDD']
例子2:將列表內以'sb'結尾的字符去除,並統計剩下字符的長度
names=['aaa','bbb_sb','ccc_sb','ddd']
names=[len(name) for name in names if not name.endswith('sb')]
print(names)
[3, 3]
2、字典生成式
例子:將字典裏面的元組改爲key:value的形式
dic={('aaa','123'),('bbb','223'),('ccc','323')}
dic={k:y for k,y in dic}
print(dic)
{'aaa': '123', 'ccc': '323', 'bbb': '223'}
3、生成器表達式 -- 拿到一個生成器對象,需要next(g)一次取一次值。
當數據量特別多的時候用,用生成器表達式
l=(n for n in range(10))
print(next(l))
print(next(l))
print(next(l))
0
1
2