a迭代的含義
迭代器即迭代的工具,那什么是迭代呢?
#迭代是一個重復的過程,每次重復即一次迭代,並且每次迭代的結果都是下一次迭代的初始值
b為何要有迭代器?
對於序列類型:字符串、列表、元組,我們可以使用索引的方式迭代取出其包含的元素。但對於字典、集合、文件等類型是沒有索引的,若還想取出其內部包含的元素,則必須找出一種不依賴於索引的迭代方式,這就是迭代器
c可迭代對象
可迭代對象指的是內置有iter方法的對象,即字符串、元組、列表、集合、字典、文件,
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__
d迭代器對象
可迭代對象執行obj.__iter__()得到的結果就是迭代器對象
而迭代器對象指的是即內置有__iter__又內置有__next__方法的對象
可迭代對象(字符串、元組、列表、集合、字典、文件)通過調用
__iter__()
方法,這里是遵循迭代器協議,將可迭代對象轉為一個迭代器,這時既可以調用
__iter__()方法又內置有__next__()方法
即為迭代器對象。迭代器對象是一個內存地址。
迭代器對象本身也可以使用__iter__()
方法
迭代器對象再次使用__iter__()
方法生成的還是迭代器對象。
例子
dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"} iter_dic = dic.__iter__() print(iter_dic) v =iter_dic.__iter__() print(v)
輸出結果
<dict_keyiterator object at 0x02191600> <dict_keyiterator object at 0x02191600>
分析:這里可以看到,對字典dic調用了__iter__()
方法,使其變成迭代器對象,再次對這個迭代器對象使用__iter__()
方法還是其本身。
e迭代器協議
1.迭代器協議是指:對象必須提供一個next方法,執行該方法要么返回迭代中的下一項,要么就引起一個StopIteration異常,以終止迭代 (只能往后走不能往前退)
2.可迭代對象:實現了迭代器協議的對象(如何實現:對象內部定義一個iter()方法)
3.協議是一種約定,可迭代對象實現了迭代器協議,python的內部工具(如for循環,sum,min,max函數等)使用迭代器協議訪問對象。
f注意:
迭代器對象一定是可迭代對象,而可迭代對象不一定是迭代器對象。
例子
s = "hello" iter_s = s.__iter__() #將字符串用__iter__()方法轉換為迭代器對象 print(iter_s.__next__()) #調用__next__()方法依次按照順序打印每個字符 print(iter_s.__next__()) print(iter_s.__next__()) print(iter_s.__next__()) print(iter_s.__next__()) print(iter_s.__next__()) #拋出異常StopIteration,或者說結束標志
輸出結果
h e l l o #拋出異常StopIteration,或者說結束標志,StopIteration
這里等同於用for循環打印
s = "hello" for i in s: #for i in s.__iter__() print(i) #print(iter_s.__next__())直到出現StopIteration,然后結束循環
分析:這里的for 循環里的for i in s,s調用了__iter__()
方法,將s變為一個迭代器對象,同時對這個迭代器對象使用
__next__()方法
打印出來,循環訪問,並處理了最后的StopIteration,結束了循環。
小知識
next()方法是調用python解釋器的,等同於某個可迭代對象下的__next__()
方法
即
#print(next(iter_s))等同於print(iter_s.__next__())
例子2
dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"} iter_dic = dic.__iter__() print(iter_dic.__next__()) print(iter_dic.__next__()) print(iter_dic.__next__()) print(iter_dic.__next__()) # print(iter_dic.__next__()) 產生StopIteration停止標志
輸出結果
k1 k2 k3 k4
改成for循環
dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"} for i in dic: #dic調用了__iter__方法,將其改成迭代器對象 print(i) #使用__next__()方法挨個去打印,直到出現StopIteration結束
用while循環實現
dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"} iter_dic = dic.__iter__() while True: try: print(iter_dic.__next__()) except StopIteration: print("迭代結束了,循環終止") break
輸出結果
k1 k2 k3 k4 #迭代結束了,循環終止
g迭代器的優缺點
優點:
-
提供一種統一的、不依賴於索引的迭代方式
-
惰性計算,節省內存
缺點:
-
無法獲取長度(只有在next完畢才知道到底有幾個值)
-
一次性的,只能往后走,不能往前退
二、三元運算
三元表達式的格式
為真時的結果 if 判定條件 else 為假時的結果
如果條件成立,返回if前面的結果,否則else 返回else后的結果
例子
a = 2 b = 3 s = a if a < b else b #這里的if語句后不加冒號 print(s)
輸出結果
2
例子2
name = input('姓名>>: ') res = 'SB' if name == 'ken' else 'NB' print(name,res)
三、列表解析式
列表解析是Python迭代機制的一種應用,它常用於實現創建新的列表,返回的是一個列表,因此用在[]中。
例子
生成1-100以內的偶數
普通使用for循環的方式
li = [] for i in range(1,101): if i % 2 == 0: li.append(i) else: pass print(li)
使用列表解析式
li = [] res = [i for i in range(1,101)if i % 2 == 0 ] print(res)
例子2
將字符串變大寫組成列表
普通方式
s = "nicholas" li = [] for i in s : res = i.upper() li.append(res) print(li)
列表解析式
s = "nicholas" li = [i.upper() for i in s ] print(li)
四、生成器
如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
要創建一個generator,有很多種方法。
第一種方法很簡單,只要把一個列表生成式的[]
改成()
,就創建了一個generator,即生成器表達式:
生成器表達式能做的事情列表解析基本都能處理,只不過在需要處理的序列比較大時,列表解析比較費內存。
例子
li = [i*i for i in range(10) ] print(li)
輸出結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
分析:這里是生成了一個0到9的平方的列表。
這里要改成生成器只要把列表生成式的[]
改成()
li = [i*i for i in range(10) ] g = (i*i for i in range(10) ) print(li) print(g)
輸出結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] <generator object <genexpr> at 0x02221600>
分析:這里的<generator object <genexpr> at 0x02221600>就是一個生成器。這是生成器的第一種形式。
生成器是包含有__iter__()
和__next__()
方法的,所以可以直接使用for來迭代
在這里調用__next__()方法
或者用next()直接打印
li = [i*i for i in range(5) ] g = (i*i for i in range(5) ) print(li) print(g) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(next(g)) print(next(g)) #print(next(g)) #執行到此處就會產生一個StopIteration錯誤,類似可迭代對象調用__next__()一樣
輸出結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] <generator object <genexpr> at 0x02211600> 0 1 4 9 16
這里的g生成器就是一個迭代器。
這里也可以用for循環直接打印
li = [i*i for i in range(5) ] g = (i*i for i in range(5) ) print(li) for i in g: print(i)
輸出結果
[0, 1, 4, 9, 16] 0 1 4 9 16
分析:同樣的這里的對生成器g的for循環自動處理了StopIteration錯誤,結束了for循環。與處理可迭代對象的方式類似。
第二種是生成器函數:
在函數中如果出現了yield關鍵字,那么該函數就不再是普通函數,而是生成器函數。生成器函數可以生產一個無線的序列,這樣列表根本沒有辦法進行處理。
yield 的作用就是把一個函數變成一個 generator,帶有 yield 的函數不再是一個普通函數,Python 解釋器會將其視為一個 generator。函數名+括號就變成了生成器。
例子
下面為一個可以生產奇數的生成器函數。
def num(): n=1 while True: print("函數內的第一處",n) yield n print("函數內奇數", n) n+=2 print("函數內的第二處", n) new_num = num() print(new_num) next(new_num) print("函數外面的",next(new_num))
輸出結果
<generator object num at 0x02421660> 函數內的第一處 1 函數內奇數 1 函數內的第二處 3 函數內的第一處 3 函數外面的 3
分析:執行print(new_num)語句可以看到,這里的自定義函數是一個生成器,通過next()方法調用執行函數內部語句,這里的yield相當於return的功能,每次next()返回一個迭代值,下次next()繼續執行循環,於是函數繼續執行,直到再次遇到 yield,再次返回。看起來就好像一個函數在正常執行的過程中被 yield 中斷了數次,每次中斷都會通過 yield 返回當前的迭代值。而不是在while True語句下一次性執行完語句。
yield 與 return
return 是返回並中止函數
yield 是返回數據,並凍結當前的執行過程
next喚醒凍結的函數執行過程,繼續執行,直到遇到下一個yield
例子
def g(): yield 1 yield 2 yield 3 new_g = g() print(next(new_g)) #第一次調用next(new_g)時,會在執行完yield語句后掛起,所以此時程序並沒有執行結束,函數返回數據1,並凍結當前執行過程,等待下一個next()喚醒執行過程 print(next(new_g)) #通過next()喚醒執行過程,yield返回數據2,凍結執行過程,等待下一個next() print(next(new_g)) # print(next(new_g)) #這里如果運行上面這條語句,程序試圖從yield語句的下一條語句開始執行,發現已經到了結尾,所以拋出StopIteration異常。
輸出結果
1 2 3
分析:在一個生成器中,如果沒有return,則默認執行到函數完畢時返回StopIteration;
如果遇到return,如果在執行過程中 return,則直接拋出 StopIteration 終止迭代。
例子
def g(): yield 1 yield 2 return "a" yield 3 new_g = g() print(next(new_g))#通過next()執行函數,得到返回數據1,凍結當前過程,等待下一個next()喚醒 print(next(new_g))#通過next()執行函數,得到返回數據1,凍結當前過程,等待下一個next()喚醒 print(next(new_g))#通過next()執行函數,遇到return語句,直接拋出StopIteration 終止迭代,這樣yield '3'語句永遠也不會執行。 print(next(new_g))
輸出結果
1 Traceback (most recent call last): 2 File "D:/exercise/test1.py", line 11, in <module> print(next(new_g)) StopIteration: a #如果在return后返回一個值,那么這個值為StopIteration異常的說明,不是程序的返回值。
如果在return后返回一個值,那么這個值為StopIteration異常的說明,不是程序的返回值。
生成器沒有辦法使用return來返回值。
例子
與上面的例子基本相同,只是修改了return的返回值
def g(): yield 1 yield 2 return "some" yield 3 new_g = g() print(next(new_g)) print(next(new_g)) print(next(new_g)) print(next(new_g))
輸出結果
1 Traceback (most recent call last): 2 File "D:/exercise/test1.py", line 11, in <module> print(next(new_g)) StopIteration: some #生成器return返回的值是為StopIteration異常的說明,不是程序的返回值。
生成器支持的方法
close()
手動關閉生成器函數,后面的調用會直接返回StopIteration異常。
例子
def g(): yield 1 yield 2 yield 3 new_g = g() print(next(new_g)) print(next(new_g)) new_g.close() #這里通過cloes()方法直接關閉了生成器,后續通過next()也無法喚醒生成器繼續執行返回數據,也就是說無法返回數據3,在這里直接拋出StopIteration異常 print(next(new_g)) print(next(new_g))
輸出結果
1 Traceback (most recent call last): 2 File "D:/exercise/test1.py", line 11, in <module> print(next(new_g)) StopIteration
send()方法
生成器函數最大的特點是可以接受外部傳入的一個變量,並根據變量內容計算結果后返回。
例子
def gen(): value = 0 while True: receive = yield value if receive == "stop": break value = 'got: %s' % receive g=gen() print(g.send(None)) print(g.send('aaa')) print(g.send(3)) print(g.send('stop'))
輸出結果
Traceback (most recent call last): File "D:/exercise/test6.py", line 18, in <module> print(g.send('stop')) StopIteration 0 got: aaa got: 3
分析:
執行過程:
1、首先g.send(None)或者g.next()喚醒生成器,並執行到receive = yield value語句,凍結執行過程,等下一個next()、g.send()、g.close(),此時,執行完了yield語句,但是沒有給receive賦值。
注意:在啟動生成器函數時只能send(None)或者next(g),如果試圖輸入其它的值都會得到錯誤提示信息。
2、通過g.send('aaa'),會傳入aaa,並賦值給receive,然后計算出value的值,並回到while頭部,執行yield value語句有停止。
此時yield value會輸出"got: aaa",然后凍結執行狀態。
3、通過g.send(3),會重復第2步,最后輸出結果為"got: 3"
4、通過g.send('stop'),傳入生成器函數,賦值給receive,執行if receive == "stop":break 退出循環,整個函數執行完畢,最后出現StopIteration異常
throw()
throw()用來向生成器函數送入一個異常,可以結束系統定義的異常,或者自定義的異常。throw()后直接拋出異常並結束程序,或者消耗掉一個yield,或者在沒有下一個yield的時候直接進行到程序的結尾。
例子
def gen(): while True: try: yield 'normal value' yield 'normal value 2' print('here') except ValueError: print('we got ValueError here') except TypeError: break g=gen() print(next(g)) print(g.throw(ValueError)) print(next(g)) print(next(g)) print(next(g)) print(g.throw(TypeError))
輸出結果
normal value we got ValueError here normal value normal value 2 here normal value normal value 2 Traceback (most recent call last): File "D:/exercise/test6.py", line 22, in <module> print(g.throw(TypeError)) StopIteration
分析:
執行過程
1、通過print(next(g))喚醒生成器,執行到yield 'normal value',返回"normal value"被輸出,凍結生成器函數執行過程
2、執行print(g.throw(ValueError))語句,出現了ValueError,這里直接執行了except ValueError下的內容,循環繼續,返回try語句,執行了yield 'normal value',這里又輸出了一個“normal value”,之后凍結執行過程
3、執行了print(next(g)),函數里執行yield 'normal value 2'語句,輸出“normal value 2”,凍結執行過程,
4、執行了print(next(g)),函數里開始繼續執行,輸出“here”,之后調到開始執行yield 'normal value',輸出“normal value”,凍結執行過程
5、執行了print(next(g)),函數里執行yield 'normal value 2',輸出“normal value 2”,凍結執行過程
6、通過print(g.throw(TypeError)),跳出try語句,出現TypeError,直接執行except TypeError:
跳出while循環,到達生成器函數結尾,所以拋出StopIteration異常。