可迭代對象
字符串、列表、元祖、集合、字典都是可迭代的,數字是不可迭代的。(可以用for循環遍歷取出內部元素的就是可迭代的)
如何查看一個變量是否為可迭代:
from collections import Iterable l = [1,2,3,4] t = (1,2,3,4) d = {1:2,3:4} s = {1,2,3,4} print(isinstance(l,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(s,Iterable)) #結果為True就是可迭代,False就是不可迭代
可以被迭代要滿足的要求就叫做可迭代協議。可迭代協議的定義就是內部實現了__iter__方法,即可迭代對象中封裝有__iter__方法。
迭代器
迭代器:用變量調__iter__后就可以生成一個迭代器,迭代器遵循迭代器協議:必須擁有__iter__方法和__next__方法。
l = [1,2,3,4] l_iter = l.__iter__()#l_iter只是一個接受的變量
item = l_iter.__next__()#利用迭代器取值
print(item)#1
item = l_iter.__next__() print(item)#2
item = l_iter.__next__() print(item)#3
item = l_iter.__next__() print(item)#4
item = l_iter.__next__() print(item)#超出限度,報錯
上步在最后出現了報錯情況,為了使程序不報錯,可以在取完了的最后將其終止掉:
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
生成器
生成器:(本質就是一個迭代器,不過是由程序員寫出來的才叫生成器,內置的就叫迭代器)
1.生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行,惰性。
2.生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
簡易生成器:
import time def func(): a = 1
print('現在定義了a變量') yield a b = 2
print('現在又定義了b變量') yield b g1 = func() print('g1 : ',g1) #打印g1可以發現g1就是一個生成器
print('-'*20) #我是華麗的分割線
print(next(g1)) time.sleep(1) #sleep一秒看清執行過程
print(next(g1))#每print一次next才會出來一個yield的值,不然就掛在上一個yield上不繼續執行
生成器有什么好處呢?就是不會一下子在內存中生成太多數據,只有在你要的時候才會給你你要的數據
生成器應用的幾個小栗子:
有關衣服訂單:

def produce(): """生產衣服"""
for i in range(2000000): yield "生產了第%s件衣服"%i product_g = produce() print(product_g.__next__()) #要一件衣服
print(product_g.__next__()) #再要一件衣服
print(product_g.__next__()) #再要一件衣服
num = 0 for i in product_g: #要一批衣服,比如5件
print(i) num +=1
if num == 5: break
#到這里我們找工廠拿了8件衣服,我一共讓我的生產函數(也就是produce生成器函數)生產2000000件衣服。 #剩下的還有很多衣服,我們可以一直拿,也可以放着等想拿的時候再拿
生成器監聽文件輸入的栗子:

import time def tail(filename): f = open(filename) f.seek(0, 2) #從文件末尾算起
while True: line = f.readline() # 讀取文件中新的文本行
if not line: time.sleep(0.1) continue
yield line tail_g = tail('tmp') for line in tail_g: print(line)
計算移動平均值(類似於年化收益):

def averager(): total = 0 day = 0 average = 0 while True: term = yield average total += term day += 1 average = total/day g_avg = averager() next(g_avg) print(g_avg.send(10)) print(g_avg.send(12)) print(g_avg.send(13))
yield from可以在實行for循環的效果的同時將代碼變少:
def gen1(): for c in 'AB': yield c for i in range(3): yield i print(list(gen1()))#['A','B',1,2,3]
#簡化版本
def gen2(): yield from 'AB'
yield from range(3) print(list(gen2()))#['A','B',1,2,3]
列表推導式和生成器表達式:(這里用一個小故事講解知識點)

#為了彰顯高富帥本質,一口氣買了十個茶葉蛋,將他們依次排開並編號,拍照發到朋友圈 egg_list=['茶葉蛋%s' %i for i in range(10)] #列表解析 #可是這十個茶葉蛋一口氣吃不完啊,要吃也就是一個一個吃,那么就吃一個拍一個照吧 eat=('茶葉蛋%s' %i for i in range(10))#生成器表達式 print(eat) print(next(eat)) #next本質就是調用__next__ print(eat.__next__()) print(next(eat))
總結:
1.把列表解析的[]換成()得到的就是生成器表達式
2.列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
3.Python使用迭代器協議,讓for循環變得更加通用。大部分內置函數,也是使用迭代器協議訪問對象的。
附:與生成器相關的面試題:

def demo(): for i in range(4): yield i g=demo() g1=(i for i in g) g2=(i for i in g1) print(list(g1))#[0,1,2,3]
print(list(g2))#[]

def add(n,i): return n+i def test(): for i in range(4): yield i g=test() for n in [1,10]: g=(add(n,i) for i in g) print(list(g))#[20,21,22,23]