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等
xxxxxxxxxx
f = [1, 2, 3, 4]
for i in f:
print(i)
輸出結果:
xxxxxxxxxx
1
2
3
4
對象實現了__iter__()
方法
__iter__()
方法返回了迭代器對象
先通過__iter__()
獲取可迭代對象的迭代器
對獲取到的迭代器不斷調用__next__()
方法來獲取下一個值並將賦值給臨時變量
isinstance(o, t)
o:對象
t:類型,可以是直接或者間接類名、基本類型或者元組
xxxxxxxxxx
from collections.abc import Iterable
g = [1, 2, 3]
print(isinstance(g, Iterable))
print(isinstance(g, (int, float)))
輸出結果:
xxxxxxxxxx
True
False
是一個可以記住遍歷位置的對象;在上次停留的位置繼續去做一些事情
💡 兩個關鍵函數:
iter()
:獲取可迭代對象的迭代器
next()
:一個個去取元素,取完元素後會引發一個異常
xxxxxxxxxx
li = [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>
1
2
3
4
5
步驟:
iter()調用對象的__iter__()
,並把__iter__()
方法的返回結果作為自己的返回值
next()調用對象的__next__()
,一個一個地取元素
所有元素都被取完了,__next__()
將會引發StopIteration
異常
第二種方式:
xxxxxxxxxx
li = [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))
輸出結果:
xxxxxxxxxx
li2: <list_iterator object at 0x1024ba560>
1
2
3
4
5
凡是可以作用於for循環的都屬於可迭代對象
凡是可以作用於next()的都屬於迭代器
xxxxxxxxxx
from collections.abc import Iterable, Iterator
name = 'Victoria'
print(f'name is iterable: {isinstance(name, Iterable)}') # True
print(f'name is iterator: {isinstance(name, Iterator)}') # False
# 可迭代對象並不一定是迭代器對象
name2 = iter(name) # 將name轉換成迭代器對象
print(f'name2 is iterable: {isinstance(name2, Iterable)}') # True
print(f'name2 is iterator: {isinstance(name2, Iterator)}') # True
# 迭代器對象一定是可迭代對象
輸出結果:
xxxxxxxxxx
name is iterable: True
name is iterator: False
name2 is iterable: True
name2 is iterator: True
💡 總結:
可迭代對象可以通過iter()
方法轉換成迭代器對象
如果一個對象擁有__iter__()
,是可迭代對象
如果一個對象擁有__next__()
和__iter__()
方法,是迭代器對象
dir()
:查看對象中的屬性和方法
查詢例子:
xxxxxxxxxx
name = '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__()
方法
⚠️⚠️
⚠️⚠️ 錯誤示範:
xxxxxxxxxx
class Test(object):
# 初始值是1,逐步遞增
def __init__(self):
self.num = 1
def funa(self):
print(self.num)
self.num += 1
te = Test()
print(te)
for i in range(5):
te.funa()
# 但te不是迭代器類,不能for i in te:
輸出結果:
xxxxxxxxxx
<__main__.Test object at 0x104709150>
1
2
3
4
5
✅正確例子:
xxxxxxxxxx
class 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.num
mi = MyIterator()
# print(mi)
# print(next(mi))
for i in mi:
print(i)
輸出結果:
xxxxxxxxxx
1
2
3
4
5
6
7
8
9
10
Python中,一邊循環、一邊計算的機制,叫做生成器
Python提供的一種非常簡便的語法能讓我們來自己寫出迭代器
使用yield的函數被稱為生成器(generator)
xxxxxxxxxx
for 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 = ' | ')
輸出結果:
xxxxxxxxxx
0
5
10
15
20
[0, 5, 10, 15, 20]
<generator object <genexpr> at 0x101008110>
0 | 5 | 10 | 15 | 20
💡 Python中,使用yield的函數被稱為生成器函數
yield的作用:
類似return,將指定值或者多個值返回給調用者
yield的語句一次返回一個結果,在每個結果中間,掛起函數,執行__next__()
函數,再重新掛起點繼續往下執行
簡單來說就是:使函數中斷,並保存中斷的狀態
⚠️⚠️
⚠️⚠️錯誤示範:
xxxxxxxxxx
def test():
li = []
li.append('a')
li.append('b')
print('li: ',li)
test()
test()
# 局部變量每次調用都會清空,所以兩次調用一樣
輸出結果:
xxxxxxxxxx
li: ['a', 'b']
li: ['a', 'b']
⚠️⚠️
⚠️⚠️(效果出來了,但沒有使用yield函數):
xxxxxxxxxx
li = []
def test():
global li
li.append('a')
li.append('b')
print('li: ',li)
test()
test()
# 這是其中一種寫法,但是最好可以在`def test()`內有`li = []`代碼
輸出結果:
xxxxxxxxxx
li: ['a', 'b']
li: ['a', 'b', 'a', 'b']
xxxxxxxxxx
def 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
xxxxxxxxxx
def 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>
0
1
2
3
4
li: [0, 1, 2, 3, 4]
可迭代對象⫌迭代器⫌生成器
可迭代對象:指實現了python迭代協議,可以通過for ... in ...循環遍歷的對象,比如list、dict、str等,以及迭代器與生成器
迭代器:可以記住自己遍歷位置的對象,直觀體現就是可以使用next()函數返回值。迭代器只能往前,不能往後。當遍歷完畢後,next()會抛出異常
⚠️迭代器不一定是生成器
生成器:是特殊的迭代器。是Python提供的通過簡便方法寫出迭代器的一種手段