18 - 迭代器&生成器📄目錄1 可迭代對象 Iterable1.1 可迭代對象的條件1.2 for循環工作原理1.3 isinstance():判斷一個對象是否可迭代對象/已知的數據類型2 迭代器2.1 可迭代對象(Iterable)和迭代器(Iterator)2.2 迭代器協議2.3 自定義迭代器類3 生成器 generator3.1 生成器表達式3.2 生成器函數3.2.1 生成器函數例子13.2.2 生成器函數例子24 三者關係導航連結:
含義:可以通過for...in...這類語句遍歷讀取數據的對象稱之為可迭代對象
遍歷(迭代):從對象中依次把多個元素取出的過程
類據類型:str、list、tuple、dict、set等

xxxxxxxxxxf = [1, 2, 3, 4]for i in f: print(i)輸出結果:
xxxxxxxxxx1234對象實現了__iter__()方法
__iter__()方法返回了迭代器對象
先通過__iter__()獲取可迭代對象的迭代器
對獲取到的迭代器不斷調用__next__()方法來獲取下一個值並將賦值給臨時變量
isinstance(o, t)
o:對象
t:類型,可以是直接或者間接類名、基本類型或者元組

xxxxxxxxxxfrom collections.abc import Iterableg = [1, 2, 3]print(isinstance(g, Iterable))print(isinstance(g, (int, float)))輸出結果:
xxxxxxxxxxTrueFalse是一個可以記住遍歷位置的對象;在上次停留的位置繼續去做一些事情
💡 兩個關鍵函數:
iter():獲取可迭代對象的迭代器
next():一個個去取元素,取完元素後會引發一個異常

xxxxxxxxxxli = [1, 2, 3, 4, 5]# 第一步:創建迭代器對象li2 = iter(li)print(li2)# 第二步:獲取下一條數據print(next(li2))print(next(li2))print(next(li2))print(next(li2))print(next(li2))# print(next(li2)) # 第三步:取完元素後,再使用next()會引發異常輸出結果:
xxxxxxxxxx<list_iterator object at 0x1029da560>12345步驟:
iter()調用對象的__iter__(),並把__iter__()方法的返回結果作為自己的返回值
next()調用對象的__next__(),一個一個地取元素
所有元素都被取完了,__next__()將會引發StopIteration異常

第二種方式:
xxxxxxxxxxli = [1, 2, 3, 4, 5]li2 = li.__iter__()print(f'li2: {li2}')print(next(li2))print(next(li2))print(next(li2))print(next(li2))print(next(li2))輸出結果:
xxxxxxxxxxli2: <list_iterator object at 0x1024ba560>12345凡是可以作用於for循環的都屬於可迭代對象
凡是可以作用於next()的都屬於迭代器

xxxxxxxxxxfrom collections.abc import Iterable, Iteratorname = 'Victoria'print(f'name is iterable: {isinstance(name, Iterable)}') # Trueprint(f'name is iterator: {isinstance(name, Iterator)}') # False# 可迭代對象並不一定是迭代器對象name2 = iter(name) # 將name轉換成迭代器對象print(f'name2 is iterable: {isinstance(name2, Iterable)}') # Trueprint(f'name2 is iterator: {isinstance(name2, Iterator)}') # True# 迭代器對象一定是可迭代對象輸出結果:
xxxxxxxxxxname is iterable: Truename is iterator: Falsename2 is iterable: Truename2 is iterator: True💡 總結:
可迭代對象可以通過iter()方法轉換成迭代器對象
如果一個對象擁有__iter__(),是可迭代對象
如果一個對象擁有__next__()和__iter__()方法,是迭代器對象
dir():查看對象中的屬性和方法

查詢例子:
xxxxxxxxxxname = 'Victoria'name2 = iter(name)print(dir(name))print(dir(name2))輸出結果:
xxxxxxxxxx['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']對象必須提供一個next方法,執行該方法要麼就返回迭代中的下一項,否則就引發StopIteration異常,來終止迭代
兩個特性:擁有__next__()和__iter__()方法
⚠️⚠️
⚠️⚠️ 錯誤示範:
xxxxxxxxxxclass Test(object): # 初始值是1,逐步遞增 def __init__(self): self.num = 1 def funa(self): print(self.num) self.num += 1te = Test()print(te)for i in range(5): te.funa()# 但te不是迭代器類,不能for i in te:輸出結果:
xxxxxxxxxx<__main__.Test object at 0x104709150>12345
✅正確例子:
xxxxxxxxxxclass MyIterator(object): def __init__(self): self.num = 0 def __iter__(self): return self # 返回當前迭代器類的實例對象 def __next__(self): if self.num == 10: raise StopIteration('終止迭代,數據已經被取完了') self.num += 1 return self.nummi = MyIterator()# print(mi)# print(next(mi))for i in mi: print(i)輸出結果:
xxxxxxxxxx12345678910Python中,一邊循環、一邊計算的機制,叫做生成器
Python提供的一種非常簡便的語法能讓我們來自己寫出迭代器
使用yield的函數被稱為生成器(generator)

xxxxxxxxxxfor i in range(5): print(5*i)li = [i * 5 for i in range(5)]print(li)gen = (i * 5 for i in range(5)) # 列表推導式的[]改成()就成了生成器表達式print(gen)print(next(gen),next(gen),next(gen),next(gen),next(gen), sep = ' | ')輸出結果:
xxxxxxxxxx05101520[0, 5, 10, 15, 20]<generator object <genexpr> at 0x101008110>0 | 5 | 10 | 15 | 20💡 Python中,使用yield的函數被稱為生成器函數
yield的作用:
類似return,將指定值或者多個值返回給調用者
yield的語句一次返回一個結果,在每個結果中間,掛起函數,執行__next__()函數,再重新掛起點繼續往下執行
簡單來說就是:使函數中斷,並保存中斷的狀態
⚠️⚠️
⚠️⚠️錯誤示範:
xxxxxxxxxxdef test(): li = [] li.append('a') li.append('b') print('li: ',li)test()test()# 局部變量每次調用都會清空,所以兩次調用一樣輸出結果:
xxxxxxxxxxli: ['a', 'b']li: ['a', 'b']
⚠️⚠️
⚠️⚠️(效果出來了,但沒有使用yield函數):
xxxxxxxxxxli = []def test(): global li li.append('a') li.append('b') print('li: ',li)test()test()# 這是其中一種寫法,但是最好可以在`def test()`內有`li = []`代碼輸出結果:
xxxxxxxxxxli: ['a', 'b']li: ['a', 'b', 'a', 'b']
xxxxxxxxxxdef gen(): print('開始了') yield 'a' # 返回一個'a',並暫停函數,在此起掛起,下一次再從此處恢復運行 yield 'b' yield 'c'gen_01 = gen()print(gen_01) # 使用了yield函數,就是生成器函數print(next(gen_01), next(gen_01), next(gen_01),'生成器已結束') # 從對象中取值,再次取就是下一個值print(next(gen()), next(gen()), next(gen())) # (⚠️錯誤參考)加引號就是調用函數,所以每次都一樣輸出結果:
xxxxxxxxxx<generator object gen at 0x102afcac0>開始了a b c 生成器已結束開始了開始了開始了a a a
xxxxxxxxxxdef gen2(n): li = [] a = 0 while a < n: li.append(a) yield a a += 1 print(f'li: {li}')print(gen2(5))for i in gen2(5): print(i)輸出結果:
xxxxxxxxxx<generator object gen2 at 0x102300e40>01234li: [0, 1, 2, 3, 4]可迭代對象⫌迭代器⫌生成器
可迭代對象:指實現了python迭代協議,可以通過for ... in ...循環遍歷的對象,比如list、dict、str等,以及迭代器與生成器
迭代器:可以記住自己遍歷位置的對象,直觀體現就是可以使用next()函數返回值。迭代器只能往前,不能往後。當遍歷完畢後,next()會抛出異常
⚠️迭代器不一定是生成器
生成器:是特殊的迭代器。是Python提供的通過簡便方法寫出迭代器的一種手段