迭代器,生成器

silence^發表於2024-04-17

可迭代物件

【1】什麼叫迭代?

迭代其實就是更新換代,每一次迭代的過程都需要依賴上一次的結果

例如

# 屬於迭代,每次結果都依據上一次結果
# 例一
n = 1
while True:
    print(n)
    n +=1
 
# 例二    
l1 = [1,2,3,4,5,6,7,8,9]
n = 0
while n < len(l1):
    print(l1[n])
    n += 1

【2】什麼是可迭代物件

1.內建有__iter__方法的都可以稱為可迭代物件
2.__iter__該型別程式碼的讀法>>>:雙下iter方法

【3】哪些資料是可迭代物件

依次列舉並嘗試呼叫雙下iter方法即可

可迭代(支援for迴圈):字串(str),字典(dict),元組(tuple),列表(list),集合(set)

不可迭代:整型(int), 浮點型(float),布林型(bool)

迭代器物件

【1】什麼是迭代器物件

可迭代物件呼叫雙下iter方法之後生成的結果就是迭代器物件

【2】迭代器物件的特徵

含有雙下iter方法又含有雙下next方法

含有__iter__又含有__next__
s=[1,2,3,4,5,6,7,8,9,10]
res = s.__iter__()
print(res) # <list_iterator object at 0x000001BAE2829780>
'''
迭代器物件 iterator object
'''
pice = res.__next__()
print(pice)
# 1       輸出一次 依次彈出列表中的一個元素

【3】如何理解迭代器物件

迭代器物件能夠極大的節省儲存空間

  • eq:類似於哆啦A夢的口袋,不用就是一個口袋面積,用的時候可以從中取出很多資料

【4】迭代器物件如何取值

呼叫雙下next方法即可,如果取完了就會報錯

【5】擴充瞭解

1.有很多雙下方法其實都有簡便寫法,但不是全部

'''
__方法名__ 等價  方法名()
       最常見的兩個是
         __iter__   iter()
         __next__   next()
'''

2.有一些可迭代物件本身也是迭代器物件:>>> 檔案物件

3.可迭代物件呼叫一次雙下iter方法程式設計迭代器物件,如果繼續呼叫,結果還是迭代器物件本身

for迴圈內部原理

'''
for迴圈底層原理
	for 變數名 in 可迭代物件:
		迴圈體程式碼
1.會將in後面的資料呼叫__iter__()變成迭代器物件
所以就解釋了為什麼檔案物件也可以for迴圈,因為本身就是迭代器物件,再次呼叫不變
2.針對產生的迭代器物件依次呼叫__next__()方法迭代取值
3.當取值完之後,會自動處理報錯並退出迴圈
'''
user_dict = {'name':'silence','age':18,'password':123}
# 1.先轉換成迭代器物件
# res= user_dict.__iter__()
res = iter(user_dict)
# 2. 迭代取值
while True:
    # print(res.__next__())
    try:
        print(next(res))
    except Exception as e:
        # 結束while迴圈
        break
        
# name
# age
# password

迭代取值與索引取值的對比

1.索引取值

1.優勢:

  • 可以反覆獲取相同元素,並沒有固定的方向

2.劣勢:

  • 只能支援有序的容器型別,無序的無法取值相容性沒有迭代取值高

2.迭代取值

1.優勢:

  • 相容所有的容器型別
  • 不使用索引進行取值
  • 字典是不支援索引取值的
  • 取到這個值的時候就會儲存到當前的狀態,下一次從這個位置開始向下取值

2.劣勢:

  • 取值的順序永遠都是從左往右,並且無法重複獲取,取完就完了
  • 除非取值取到盡頭,否則永遠不知道終止索引在哪裡
  • 迭代同一個物件只能重新建立
  • 呼叫 next 取值的前提是 已經生成並且得到了一個迭代器物件

生成器物件(自定義迭代器)

def index():
    print('你還記得我嘛')
    # yield 123
    yield 123,111
# 沒有呼叫之前就是一個普通的函式
'''
當函式體程式碼中含有yield關鍵字
    第一次呼叫函式並不會執行函式體程式碼
    而是將函式變成了生成器
'''
# print(index) # <function index at 0x000001FF8B493E20>
# 加括號呼叫並接收結果:不執行程式碼,而是變成生成器物件(迭代器)
res = index()
# print(res)  #<generator object index at 0x00000204F3AE6960> 變成生成器物件
# 變成生成器之後呼叫__next__就會開始執行函式體程式碼
# print(next(res)) #123 yield有點像return功能
print(next(res))  #(123, 111)
'''
生成器物件也是節省儲存空間的 特性與迭代器物件一致
'''
def index():
    print('你還記得我嘛')
    yield 123
    print('是不是忘記我了')
    yield 123,111
'''
如果函式體程式碼中含有多個yield關鍵字,每執行一次__next__可以返回後面的值,並且讓程式碼停留在yield位置
再次執行__next__基於上次的位置往後執行到下一個yield關鍵字處
如果沒有了,再執行就會報錯 StopIteration
'''
res = index()

print(next(res)) # 123
print(next(res)) # (123,111)
print(next(res))  # 報錯
# 用for迴圈也可以 直接for自帶報錯處理
def index():
   
    yield 123
    
    yield 123,111

res = index()
for i in res: 
    print(i)
#123
#(123, 111)

自定義range方法

range方法其實是一個可迭代物件


'''
需求:透過生成器模擬range方法
for i in range(1,10):
    print(i)
'''
# 先以兩個引數的range引數為例
def my_range(start, end):
    while start < end:
        yield start
        start += 1
for i in my_range(1,10):
    print(i)
# 初步實現之後 在考慮不同引數情況 一個引數 三個引數
# 先針對一個
'''
end可以不傳值 應該設定成預設引數 end=None
程式碼層面做判斷 將形引數據做替換處理
        end=start
        start=0 
'''
def my_range(start, end=None):
    if not end:
        end = start
        start = 0
    while start < end:
        yield start
        start += 1
for i in my_range(10):
    print(i)
    
# 針對三個引數情況
'''
給函式新增第三個形參 並且設定成預設引數 預設值是1 step=1
每次遞增只需要遞增step的數值即可
'''


def my_range(start, end=None, step=1):
    if not end:
        end = start
        start = 0
    while start < end:
        yield start
        start += step


for i in my_range(1, 10, 2): 
    print(i)# 1,3,5,7,9
for i in my_range(10):
    print(i)# 0,1,2,3,4,5,6,7,8,9
for i in my_range(1,10):
    print(i) # 1,2,3,4,5,6,7,8,9

yield關鍵字作用

1.在函式體程式碼中出現 可以將函式變成生成器

2.在執行過程中,可以將後面的值返回出去,類似於return

3.還可以暫停住程式碼的執行

4.還可以接收外界的傳值

yield接收外界傳值 用語法.send()

def eat(name):
    print(f'{name} 準備乾飯')
    while True:
        food = yield
        print(f'{name} 正在吃{food}')
res = eat('大胃王')
# 想執行一次,如果想執行多次直至結束 可以直接用for迴圈
res.__next__()
# yield 可以接收外部傳值
res.send('魚香肉絲')  # 可以給yield傳值,並且自動呼叫一次__next__方法
res.send('宮保雞丁')
# 大胃王 準備乾飯
# 大胃王 正在吃魚香肉絲
# 大胃王 正在吃宮保雞丁

生成器表示式

也是為了節省儲存空間

重點:

生成器內部的程式碼只有在呼叫雙下next迭代取值的時候才會執行

面試題示例

# 普通求和函式
def add(n,i):
    return n+i

# 生成器物件 返回 1,2,3
def test():
    for i in range(4):
        yield i
# 將test函式變成生成器物件
g=test()
# 簡單for迴圈
for n in [1,10]:
    g = (add(n,i) for i in g)
    '''
    從上到下還沒有呼叫函式 只有執行兩次for迴圈,只有對g進行迭代取值是才進行程式碼操作
    第一次for迴圈
     g = (add(n,i) for i in g)
     第二次for迴圈
      g = (add(n,i) for i in  (add(n,i) for i in g))
      再list(g)呼叫函式時 第二次for迴圈裡的n都為10,而i是0
      所以第一次呼叫的時候就是10+10  20是起始數
    '''
print(n)
res = list(g)# list底層就是for迴圈 相當於對g做了迭代取值操作
print(res)

# A .res=[10,11,12,13]
# B .res=[11,12,13,14]
# C .res=[20,21,22,23]  # 正確答案
# D .res=[21,22,23,24]
# 正確答案是c 訣竅就是抓n是多少即可

相關文章