迭代器
【一】迭代器介紹
- 迭代器就是用來迭代取值的工具,是重複反饋過程的程式
- 目的是為了更加逼近我們想要的目標和結果
- 每一次迭代得到的返回結果就是下一次迭代開始的初始值
num_list = [1,2,3,4,5,6]
count = 0
while count <len(num_list):
# 每一次使用的索引位置就是上一次+1後的索引位置
print(num_list[count])
count += 1
1
2
3
4
5
6
【二】可迭代物件
【1】什麼是可迭代物件
- 可以透過列表的索引進行取值,但是如果一個資料沒有索引的話
- 必須找到一種可以用索引也可以不用索引的方法來取值
【2】八大基本資料型別
補充
生成可迭代物件的兩種方式
iter()
相當於
__iter__(資料)
迭代器物件迭代的方法的兩種方式
迭代器物件.__next__()
next(迭代器物件)
數字型別
整數
age = 18
print(iter(age))
print(age.__iter__)
報錯 'int' object is not iterable
浮點數
age = 18.5
print(iter(age))
print(age.__iter__)
報錯 'float' object is not iterable
字串型別
qwer = 'qewr'
print(iter(qwer))
print(qwer.__iter__)
輸出
<str_iterator object at 0x0000021D016BA740>
<method-wrapper '__iter__' of str object at 0x0000021D0165D3B0>
列表型別
list = [1,2,3,4,5,6]
print(iter(list))
print(list.__iter__)
輸出
<list_iterator object at 0x000001FA4D7097B0>
<method-wrapper '__iter__' of list object at 0x000001FA4D6B4E40>
字典型別
dict = {'q':'1','w':2}
print(iter(dict))
print(dict.__iter__)
輸出
<dict_keyiterator object at 0x000001E457AED670>
<method-wrapper '__init__' of dict object at 0x000001E457AA1F40>
布林型別
bool = True
print(iter(bool))
print(bool.__iter__)
報錯 'bool' object is not iterable
元組型別
tuple = (1,2,3,4,5,6)
print(iter(tuple))
print(tuple.__iter__)
輸出
<tuple_iterator object at 0x000001AE3656A740>
<method-wrapper '__init__' of tuple object at 0x000001AE36578940>
集合型別
set = {1,2,3,4,5,6}
print(iter(set))
print(set.__iter__)
輸出
<set_iterator object at 0x000001E74BFF2080>
<method-wrapper '__init__' of set object at 0x000001E74C026DC0>
【3】總結
能.__iter__不報錯的就是可迭代型別 ————>可迭代物件
可迭代型別
字串、列表、元組、集合、字典
不可迭代型別
整數、浮點數、布林
【三】迭代器物件
【1】什麼是迭代器物件
# 如果呼叫obj.__iter__()返回的是一個可迭代物件
# 有的可迭代物件還有__next__()方法
# 迭代器物件實在可迭代物件(具有.__iter__()方法)的基礎上具有.__next__()方法的物件
【2】八大基本資料型別(除了不可迭代)
(1)字串
name = "qwer"
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__()) # 會報錯 most recent call last
<str_iterator object at 0x00000202DE74A740>
q
w
e
r
第五行報錯
(2)列表
name = [1,2,3,4,5,6]
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
<list_iterator object at 0x0000026B66C3A740>
1
2
3
4
5
(3)字典
得到的是鍵
name = {'q':1,'w':'2','e':'3','r':'4'}
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
<dict_keyiterator object at 0x000001C4B92DD670>
q
w
e
r
(4)元組
name = (1,2,3)
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
<tuple_iterator object at 0x000001D9C593A740>
1
2
3
(5)集合
name = {1,2,3,4,5,6}
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
<set_iterator object at 0x00000234968D2000>
1
2
3
4
【3】總結
具有__iter__() 和 __next__()方法的物件是可迭代物件
-
在八大基本資料型別中,除了數字和布林型別,其他都是迭代器物件
-
迭代器物件一定是可迭代物件,可迭代物件不一定是迭代器物件
-
迭代器物件是同時具有上述兩個方法的物件
-
可迭代物件只需有第一個方法既是可迭代物件
【四】迭代器的優缺點
- 優點
- 不使用索引取值(例如字典不支援索引取值)
- 取到值後會儲存當前狀態,下次從當前位置開始繼續取值
- 缺點
- 除非取值取到盡頭,否則永遠不知道終止索引在哪裡
- 只能取一次記一次位置,下一次的其實位置是上一次的終止位置,想回到開頭 回不去
- 呼叫 next 取值的前提是 已經生成並且得到了一個迭代器物件 iter_obj
- 迭代同一個物件只能重新建立
name = 'dr'
name_iter = name.__iter__()
print(next(name_iter))
print(next(name_iter))
name_iter = name.__iter__() # 要重複取值必須重新建立迭代器物件
print(next(name_iter))
d
r
d
生成器
【一】什麼是生成器
-
生成器是一種特殊的迭代器
-
在需要的時候給你資料,不需要就不會給資料
-
for迴圈 range(10) 可以把生成的數字取出來
-
資料量過大的話 讀取資料記憶體滿,電腦卡死
-
生成器 ---> 一次取...行 --->處理完這些資料後--->再取...行
例子 for i in fp:
【二】生成器的建立方式
【1】列表推導式
print([i for i in range(10)])
# 如果將列表生成式外面的列表換成元組,則會生成一個生成器物件
print((i for i in range(10)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
<generator object <genexpr> at 0x0000020E358F8430>
【2】yiled關鍵字
def add(i):
yield i
# add(2)進入到add函式,將原本的函式物件轉換為生成器物件
res = add(2)
print(res)
# 想取值只能使用next來迭代取值
print(res.__next__())
<generator object add at 0x00000155DB1E8430>
2
def add(i):
# i = 1
while True:
yield i # 第一次next
res = add(1)
print(res)
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
res = add(3)
print(res.__next__())
<generator object add at 0x000001EFDF0CEAB0>
1
1
1
1
3
補充
在第一次呼叫生成器時,`yield`語句會在賦值運算子`=`的右邊停下。
考慮下面的程式碼:
```python
def my_generator():
x = yield
print("Received:", x)
gen = my_generator()
```
當你執行`gen = my_generator()`時,生成器`my_generator`並沒有開始執行。相反,它返回了一個生成器物件`gen`。此時,`my_generator`函式中的程式碼並沒有執行。
然後,當你呼叫`gen.__next__()`或`next(gen)`時,生成器才會開始執行。它會執行到`x = yield`這一行,但暫停在`yield`右邊,等待接收一個值。這個值會被賦給`x`。因此,第一次呼叫`yield`語句時,生成器會停在賦值運算子`=`的右邊。
【三】生成器案例
def outer():
print("開始吃飯")
while True:
food = yield # 等同於yield(food)
print(f"做了{food},吃{food}")
res = outer()
print(res)
print(res.__next__())
print(res.__next__())
<generator object outer at 0x00000183F2538430>
開始吃飯
None
做了None,吃None
None # 原因是在生成器內部走完了上面程式碼,又回到了yield 沒有返回值
yield語句在生成器函式中充當了暫停和恢復執行的標記,同時也充當了返回值的功能。當生成器函式執行到yield語句時,它會暫停執行並將yield後面的表示式作為返回值返回給呼叫方。在上述程式碼中food為yield的後面的表示式
向生成器中傳值
send() 傳值後會再往下走一步
res = outer()
print(res)
print(res.__next__())
res.send('炒餅') # 向 yield 傳值並且讓生成器向下走一下
print(res.__next__())
res.send('炒飯')
<generator object outer at 0x000002A8C99E8430>
開始吃飯
None
做了炒餅,吃炒餅
做了None,吃None
None
做了炒飯,吃炒飯
【四】裝飾器 + 生成器
def init_iter(func):
def inner(*args,**kwargs):
# g 是得到的生成器物件
g = func(*args,**kwargs)
# 呼叫自己生成器往下走
next(g)
# 走回來的返回值返回出去
return g
return inner
@init_iter
def outer():
print("開始吃飯")
while True:
# yield food
food = yield
print(f"做了{food},吃{food}")
res = outer()
res.send('炒米粉')
res.send('炒fan')
開始吃飯
做了炒米粉,吃炒米粉
做了炒fan,吃炒fan
【五】生成器內部修改可變資料型別
def init_iter(func):
def inner(*args,**kwargs):
# g 是得到的生成器物件
g = func(*args,**kwargs)
# 呼叫自己生成器往下走
next(g)
# 走回來的返回值返回出去
return g
return inner
@init_iter
def outer():
print("開始吃飯")
list = []
while True:
# yield food
food = yield
list.append(food) 因為沒有退出迴圈,所以會把新值一直加到列表中
print(f"做了{food},吃{food}")
print(list)
res = outer()
res.send('炒米粉')
res.send('炒fan')
開始吃飯
做了炒米粉,吃炒米粉
['炒米粉']
做了炒fan,吃炒fan
['炒米粉', '炒fan']
【六】yield + next 搭配著使用
仿照迴圈自己構建迴圈函式
for i in range(5):
print(i)
def my_range(start, stop, step):
'''
:param start: 0
:param stop: 5
:param step: 1
:return:
'''
print("start")
while start < stop:
yield start
start += step
print("end")
res = my_range(0, 5, 1)
print(res) # <generator object my_range at 0x000002DF0361CBA0>
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
# 這裡會報錯 ---> 沒有可迭代的值可以被獲取了
print(res.__next__()) # StopIteration
def init_iter(func):
def inner(*args, **kwargs):
try:
generator = func(*args, **kwargs)
next(generator)
return generator
except StopIteration:
pass
return inner
def range_(start, stop, step):
while start < stop:
yield start
start += step
# for 迴圈內部做了異常捕獲
for i in range_(0,5,1):
print(i)
def my_range(start, stop, step):
def range_(start, stop, step):
while start < stop:
yield start
start += step
res = range_(start, stop, step)
while True:
try:
print(res.__next__())
except StopIteration:
break
res = my_range(start=1, stop=5, step=1)
res
0
1
2
3
4
1
2
3
4