python全棧學習總結五:叠代器和生成器
一 叠代器
1 什麽是叠代器協議
叠代器協議:對象必須提供一個next方法,執行該方法要麽返回叠代中的下一項,要麽引起一個Stoplteration異常,以終止叠代(只能往後走不能往前退)
可叠代對象:實現了叠代器協議的對象(如何實現:對象內部定義一個__iter__()方法)
協議是一種約定,可叠代對象實現了叠代器協議,python的內部工具(如for循環,sum,min,max函數等)使用叠代器協議訪問對象。
2 自定義叠代器
class MyIterator: #自定義叠代器類 def __init__(self,x = 2,xmax = 63): self.__mul,self.__x = x,x self.__xmax = xmax def __iter__(self): #定義叠代器協議方法,返回類本身 return self def __next__(self):#定義叠代器協議方法 if self.__x and self.__x !=1: self.__mul *= self.__x if self.__mul <= self.__xmax: return self.__mul else: raise StopIteration else : raise StopIteration if __name__ == ‘__main__‘: myiter = MyIterator(3,150) #實例化叠代器MyIterator for i in myiter: print("叠代的數據元素為:",i)
3 可叠代對象
字符串,列表,元組,字典,集合,文件(str,list,tuple,dict,set,file)均為可叠代對象,通過for循環調用對象內部的__iter__方法,變成可叠代對象
使用的叠代器協議函數
x = ‘hell‘
iter_x = x.__iter__() #轉化為可叠代對象,遵循叠代協議
iter_x.__next__() #轉換後,可以使用__next__()方法來進行叠代
next(iter_x)#也可以使用next函數進行叠代
for循環的實質是在調用next方法或函數
另外兩個叠代函數
iter(iterable) 參數iterable為可叠代類型
iter(callable,sentine) 參數callable是可調用類型,一般為函數,第二個參數為哨兵,當第一個參數(函數)調用返回值等於第二個參數的值時,叠代或遍歷停止。
class Counter: #定義用於計數的類 def __init__(self,x=0): #定義構造函數,初始化實例屬性 self.x = x counter = Counter() #初始化實例類Counter def used_iter():#定義用於iter()函數的函數 counter.x +=2 #修改計數類中的實例屬性的值 return counter.x for i in iter(used_iter,8): #叠代iter()函數產生的叠代器 print("本次遍歷的數值:",i)
4 for和while操作可叠代對象,輸出相同
x = ‘hello‘ for i in x: print(i) x = ‘hello‘ x_iter = x.__iter__() while True: try: print(x_iter.__next__()) except StopIteration: print("叠代循環完成!") break
二 生成器
生成器:一種數據類型,自動實現了叠代器協議(其他的數據類型需要調用自己內置的__iter__方法),所以生成器就是可叠代對象
1 三元表達式與列表解析
三元表達式:res = ‘good‘ if num == True else ‘bad‘ #當num為True時,res的結果為‘good’,否則res的結果為‘bad‘
列表解析:l = [i for i in range(10)] #生成列表l = [0,1,2,3,4,5,6,7,8,9]
l1 = [i for i in range(10) if i>5] #三元表達式與列表解析相結合。
2 生成器表達式創建
(1)生成器表達式:num = (i for i in range(10) if i > 5) #註意列表解析是用的[]而生成器表達式是()
#列表解析 ret_list = [] for i in range(10): ret_list.append(" 包子%s"%i) print(ret_list) ret_list = ["包子%s"%i for i in range(10)] print(ret_list) ret_list = ("包子%s"%i for i in range(10)) print(ret_list) print(ret_list.__next__()) print(ret_list.__next__()) print(ret_list.__next__())
註意觀察列表解析和生成器創建的區別:列表解析一次生成列表,而生成器只是創建一個生成器對象,需要調用next方法才能訪問。列表解析數據很多時,需要占用大量的內存。生成器的好處顯而易見,用的時候才生成。
#生成器創建+三元判斷 ret_list = ("包子%s"%i for i in range(10) if i > 5) print(ret_list) print(ret_list.__next__()) print(ret_list.__next__()) print(ret_list.__next__())
(2)生成器對象是通過yield關鍵字定義的函數對象,因此,生成器也是一個函數,生成器用於生成一個值的序列,以便在叠代中使用。
def myYiled(n): #定義一個生成器(函數) while n>0: print("開始生成...:") yield n #yield語句,用於返回給調用者其後表達式的值 print("完成一次...") n -= 1 if __name__ == ‘__main__‘: for i in myYiled(4): print("遍歷得到的值:",i) print(‘*‘*10) my_yield = myYiled(3) print(‘已經實例化生成器對象!‘) print(my_yield.__next__()) print(‘第二次調用__next__()方法‘) print(my_yield.__next__()) print("第三次開始調用!") print(next(my_yield))
運行現象:
yield語句是生成器中的關鍵語句,生成器在實例化時並不會立即執行,而是等待調用其__next__()方法的時候才開始執行,並且當程序運行完yield語句後就會保持(hold)住,即保持當前狀態且停止運行,等待下一次遍歷時,才恢復運行!如上圖!!!!
使用生成器,可以生成一個值的序列用於叠代,並且這個值的序列不是一次生成的,而是使用一個,再生成一個,的確可以使程序節約大量內存!
yield語句不僅可以使函數成為生成器和返回值,還可以接收調用者傳來的數值。但值得註意的是:第一次調用生成器不能傳給生成器None以外的值,否則會引發錯誤!使用生成的send方法傳值!
def myYield(n): while n > 0: rcv = yield n #rcv用來接收調用者傳來的值 n -= 1 if rcv is not None: n = rcv if __name__ == ‘__main__‘: my_yield = myYield(3) print(my_yield.__next__()) print(next(my_yield)) print(‘--‘*20) print("傳給生成器一個值,重新初始化生成器!") print(my_yield.send(5)) for i in my_yield: print(i) my_yield = myYield(3) #print(my_yield.send(4)) #xxxx第一次調用就傳值錯誤!!! print(my_yield.send(None)) #但是可以傳None print(next(my_yield)) print(my_yield.send(6))
3 生成器特性:只能取一次
利用生成器來計算總人口,從文件中讀取數據,分析出人口的總數量,並計算出所占比例
def get_population(): with open("人口統計",‘r‘,encoding=‘utf-8‘) as f: for p in f: yield p num_p = get_population() # print(num_p.__next__()) # print(next(num_p)) all_p = sum(int(eval(p)[‘population‘]) for p in num_p) #eval提取數據類型字典 print("總人口:%s"%all_p) num_p = get_population()#上面已經取完,重新調用生成器 for num in num_p: num_dict = eval(num) # print("%s的比例:%s%%"%(10,8)) print("%s的比例:%.2f%%"%(num_dict[‘name‘],int(num_dict[‘population‘])*100/all_p))
#人口統計文件
{"name":"濟南","population":"100"} {"name":"青島","population":"200"} {"name":"煙臺","population":"300"} {"name":"濰坊","population":"400"} {"name":"菏澤","population":"500"} {"name":"臨沂","population":"600"}
上面例程中,調用內置函數sum求和,使用生成器的方式計算,沒有使用列表解析的方式,節省了大量內存,同理使用max,min等類似!一定要用生成器的方法!一個一個的計算!
4 生成器與協程(生產者與消費者)
運用send()方法來重置生成器的生成序列,也成為協程,是一種解決程序並發的方法!
def consumer(): #定義一個消費者模型(生成器協程) print("等待接收處理任務...") while True: data = (yield ) #模擬接收並處理任務 print(‘收到任務!‘) #此處可以執行函數調用來完成相關任務 def producer(): c = consumer() c.__next__() for i in range(5): print("發送一個任務...‘,‘任務%d"%i) c.send(‘任務%d‘%i) if __name__ == ‘__main__‘: producer()
python全棧學習總結五:叠代器和生成器