Python迭代器、生成器、協(xié)程使用方式
什么是迭代
迭代是訪問集合元素的一種方式。(迭代和遍歷非常相似)
什么是迭代器(Iterator)
迭代器是一種對(duì)象,可以記住遍歷的位置的對(duì)象。迭代器對(duì)象從集的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。
在迭代一個(gè)可迭代對(duì)象的時(shí)候,實(shí)際上就是先獲取該對(duì)象提供的一個(gè)迭代器,然后通過這個(gè)迭代器來依次獲取對(duì)象中的每一個(gè)數(shù)據(jù)。
可迭代對(duì)象
某種數(shù)據(jù)可以通過for....in....的循環(huán)語法從其中依次拿到數(shù)據(jù)進(jìn)行使用的數(shù)據(jù)類型,這種數(shù)據(jù)類型被稱為可迭代對(duì)象
檢測(cè)某個(gè)對(duì)象是否是可迭代對(duì)象的方法isinstance(待檢測(cè)對(duì)象,Iterable)
該方法檢測(cè)待檢測(cè)對(duì)象是不是Iterable迭代類的子類
如果是返回True,如果不是返回False
from collections.abc import Iterable """ isinstance(待檢測(cè)對(duì)象,Iterable(迭代類,固定寫法)) 判斷待檢測(cè)對(duì)象是不是迭代類的一個(gè)子類 返回值:True可以迭代 False不可以迭代 """ string="fhsudfoh" result=isinstance([1,2,3],Iterable) print(result)
for循環(huán)的本質(zhì)
for循環(huán)的本質(zhì)是在對(duì)一個(gè)迭代器進(jìn)行操作。
首先,for循環(huán)在遍歷可迭代對(duì)象的時(shí)候會(huì)把可迭代對(duì)象通過函數(shù)iter()或__iter__方法返回一個(gè)迭代器,然后在對(duì)這個(gè)迭代器對(duì)象進(jìn)行操作。
迭代器可以用next()或__next__方法得到返回值,當(dāng)把所有的值取完時(shí)會(huì)拋出StopIteration錯(cuò)誤,這個(gè)錯(cuò)誤在for循環(huán)中python會(huì)自己處理,不會(huì)展示給開發(fā)者。
自定義一個(gè)列表
class MyList(object): # 重寫初始化方法,初始化一個(gè)數(shù)組用來存儲(chǔ)數(shù)據(jù) def __init__(self): self.list = [] def __iter__(self): # iter方法返回一個(gè)myIterator迭代器對(duì)象 self.myiterator = MyIterator(self.list) return self.myiterator def additer(self, values): # additer方法用于添加數(shù)據(jù) self.list.append(values) class MyIterator(object): # 初始化一個(gè)下標(biāo)和接收一個(gè)外部的數(shù)組變量 def __init__(self, my_list): self.list = my_list self.index = 0 def __iter__(self): pass # 當(dāng)對(duì)象被迭代器返回后會(huì)自動(dòng)尋找next方法,進(jìn)行迭代 def __next__(self): if self.index < len(self.list): data = self.list[self.index] self.index += 1 return data else: raise StopIteration if __name__ == "__main__": mylist = MyList() mylist.additer("張三") mylist.additer("李四") mylist.additer("王五") for value in mylist: print(value)
實(shí)現(xiàn)一個(gè)斐波那契數(shù)列
class feibonaqi(object): def __init__(self, num): self.fei_list = [] self.num = num self.a = 1 self.b = 1 self.fei_index = 0 def __iter__(self): return self def __next__(self): if self.fei_index < self.num: data = self.a self.a, self.b = self.b, self.a + self.b self.fei_index += 1 return data else: raise StopIteration if __name__ == '__main__': fei = feibonaqi(5) for i in fei: print(i)
什么是生成器(generator)
生成器是一個(gè)特殊的迭代器,使用了 yield 的函數(shù)被稱為生成器(generator)。
yield 是一個(gè)關(guān)鍵字,用于定義生成器函數(shù),生成器函數(shù)是一種特殊的函數(shù),可以在迭代過程中逐步產(chǎn)生值,而不是一次性返回所有結(jié)果。當(dāng)在生成器函數(shù)中使用 yield 語句時(shí),函數(shù)的執(zhí)行將會(huì)暫停,并將 yield 后面的表達(dá)式作為當(dāng)前迭代的值返回。然后,每次調(diào)用生成器的 next() 方法或使用 for 循環(huán)進(jìn)行迭代時(shí),函數(shù)會(huì)從上次暫停的地方繼續(xù)執(zhí)行,直到再次遇到 yield 語句。可以逐步產(chǎn)生值,而不需要一次性計(jì)算并返回所有結(jié)果。
調(diào)用一個(gè)生成器函數(shù),返回的是一個(gè)迭代器對(duì)象。
生成器對(duì)象可以使用send()方法進(jìn)行傳參。
生成器中也可以使用return,當(dāng)語句執(zhí)行到return語句之后,生成器會(huì)終止迭代,拋出停止迭代的異常。
創(chuàng)建生成器的兩種方式
使用推導(dǎo)式創(chuàng)建
result_list = (x * 2 for x in range(10)) print(result_list) # 使用生成器生成一個(gè)偶數(shù)數(shù)列[0,2,4,6,8,10,12,14,16,18] # 這邊使用next先取出第一個(gè)值,后面for循環(huán)遍歷出來的結(jié)果是從0后面開始的 value = next(result_list) print(value) for i in result_list: print(i, end=" ")
使用函數(shù)創(chuàng)建
def mygenerator(): num_list = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] num_index = 0 while True: if num_index < len(num_list): yield num_list[num_index] num_index += 1 if __name__ == '__main__': mg = mygenerator() value = next(mg) print(value) value = next(mg) print(value) value = next(mg) print(value)
使用generator實(shí)現(xiàn)斐波那契數(shù)列
def feibonaqi(num): a = 1 b = 1 count_index = 0 while count_index < num: data = a a, b = b, a + b count_index += 1 yield data if __name__ == '__main__': fei = feibonaqi(5) # data=next(fei) # print(data) # data=next(fei) # print(data) # data=next(fei) # print(data) # data=next(fei) # print(data) # data=next(fei) # print(data) for value in fei: print(value)
什么是協(xié)程
協(xié)程(coroutine)又稱為微線程,是一種用戶態(tài)的輕量級(jí)線程。
使用yield可以實(shí)現(xiàn)協(xié)程,yield可以終止任務(wù)執(zhí)行,并記錄任務(wù)執(zhí)行的進(jìn)度。
協(xié)程可以實(shí)現(xiàn)一個(gè)線程執(zhí)行多個(gè)任務(wù)
協(xié)程的基本實(shí)現(xiàn)
import time def work1(): while True: print("正在執(zhí)行work1。。。。。。。。") yield time.sleep(0.5) def work2(): while True: print("正在執(zhí)行work2.....。。。。。。。。") yield if __name__ == '__main__': w1 = work1() w2 = work2() while True: next(w1) next(w2)
使用greenlet實(shí)現(xiàn)協(xié)程
"""greenlet是一個(gè)第三方庫可以提供自行調(diào)度的微線程,只需要告訴switch如何運(yùn)行即可 1、導(dǎo)入模塊 2、創(chuàng)建greenlet對(duì)象 3、使用switch實(shí)現(xiàn)線程的調(diào)度 """ import time from greenlet import greenlet def work1(): while True: print("正在執(zhí)行work1。。。。。。。。") time.sleep(0.5) g2.switch() def work2(): while True: print("正在執(zhí)行work2...............") time.sleep(0.5) g1.switch() if __name__ == '__main__': g1 = greenlet(work1) g2 = greenlet(work2) g1.switch()
使用gevent實(shí)現(xiàn)協(xié)程
import time import gevent from gevent import monkey monkey.patch_all() """gevent也是一個(gè)第三方庫,自動(dòng)調(diào)度協(xié)程,自動(dòng)識(shí)別程序中的耗時(shí)操作 使用步驟: 1、導(dǎo)入模塊 2、指派任務(wù) g1=gevent.spawn(work1) g2=gevent.spawn(work2) 3、join()讓主線程等待協(xié)程進(jìn)行結(jié)束后在退出 注意:gevent默認(rèn)不識(shí)別time.slepp(),識(shí)別的是gevent.sleep() 可以使用打補(bǔ)丁的方式解決,猴子模塊 from gevent import monkey 破解所有monkey.patch_all() """ def work1(): while True: print("正在執(zhí)行work1。。。。。。。。") time.sleep(0.5) # gevent.sleep(0.5) def work2(): while True: print("正在執(zhí)行work2...........") time.sleep(0.5) # gevent.sleep(0.5) if __name__ == '__main__': g1 = gevent.spawn(work1) g2 = gevent.spawn(work2) g1.join() g2.join()
進(jìn)程、線程、協(xié)程的區(qū)別
進(jìn)程是資源分配的基本單位、線程是CPU調(diào)度的基本單位、協(xié)程是單線程中執(zhí)行多任務(wù)的方式
- 切換效率:協(xié)程>線程>進(jìn)程
- 高效率方式:進(jìn)程+協(xié)程
選擇問題
- 多進(jìn)程:密集CPU任務(wù),需要充分使用多核CPU資源(服務(wù)器,大量的并行計(jì)算)的時(shí)候,用多進(jìn)程
- 缺陷:多個(gè)進(jìn)程間通信成本高,切換開銷大
- 多線程:密集I/O任務(wù)(網(wǎng)絡(luò)I/O、磁盤I/O、數(shù)據(jù)庫I/O)使用多線程合適
- 缺陷:同一個(gè)時(shí)間切片只能運(yùn)行一個(gè)線程,不能做到高并行,但是可以做到高并發(fā)
- 協(xié)程:當(dāng)程序中存在大量不需要CPU的操作時(shí)(I/O),適用協(xié)程
- 缺陷:?jiǎn)尉€程執(zhí)行,處理密集CPU和本地磁盤IO的時(shí)候,性能較低。處理網(wǎng)絡(luò)I/O性能還是比較高的
使用協(xié)程下載網(wǎng)絡(luò)的圖片
""" 使用協(xié)程的方式將網(wǎng)路上的圖片下載到本地 核心方法:urllib.request.urlopen()打開網(wǎng)址并返回對(duì)應(yīng)的內(nèi)容(二進(jìn)制流) """ import urllib.request import gevent from gevent import monkey monkey.patch_all() def down_image(url_image, file_name): # 異常處理 try: # 打開網(wǎng)絡(luò)文件 request_data = urllib.request.urlopen(url_image) # 打開本地文件 with open(file_name, "wb") as file: # 循環(huán)讀取寫入文件 while True: # 從網(wǎng)絡(luò)中讀取1024個(gè)字節(jié)的數(shù)據(jù) file_data = request_data.read(1024) # 判斷數(shù)據(jù)是否為空,不為空就寫入,為空就跳出循環(huán) if file_data: file.write(file_data) else: break except Exception: print("文件%s下載失敗" % file_name) else: print("文件%s下載完成" % file_name) def main(): img_url1 = "https://img1.baidu.com/it/u=1849885514,1853800516&fm=253&fmt=auto&app=138&f=JPG?w=500&h=500" img_url2 = "https://img2.baidu.com/it/u=4137380440,3737943568&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800" img_url3 = ("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F4e3acf5c-a0fc-44a2" "-99ff-94f69cae80b4%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com" "&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1709038944&t=f8e32fd271bb9215553250f0deb5e23d") # joinall()將傳入的線程,等待運(yùn)行完成在結(jié)束進(jìn)程 gevent.joinall([ gevent.spawn(down_image, img_url1, "img1"), gevent.spawn(down_image, img_url2, "img2"), gevent.spawn(down_image, img_url3, "img3") ]) if __name__ == '__main__': main()
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
python爬蟲之自動(dòng)登錄與驗(yàn)證碼識(shí)別
這篇文章主要為大家詳細(xì)介紹了python爬蟲之自動(dòng)登錄與驗(yàn)證碼識(shí)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09利用pyecharts實(shí)現(xiàn)地圖可視化的例子
今天小編就為大家分享一篇利用pyecharts實(shí)現(xiàn)地圖可視化的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-08-08python?subprocess執(zhí)行外部命令常用方法詳細(xì)舉例
這篇文章主要給大家介紹了關(guān)于python?subprocess執(zhí)行外部命令常用方法的相關(guān)資料,Python的subprocess模塊提供了一種在Python中調(diào)用外部命令的方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12記錄一下scrapy中settings的一些配置小結(jié)
這篇文章主要介紹了記錄一下scrapy中settings的一些配置小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09python編程測(cè)試電腦開啟最大線程數(shù)實(shí)例代碼
這篇文章主要介紹了python編程測(cè)試電腦開啟最大線程數(shù)實(shí)例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02機(jī)器學(xué)習(xí)數(shù)據(jù)預(yù)處理之獨(dú)熱One-Hot編碼及其代碼詳解
獨(dú)熱編碼即 One-Hot 編碼,又稱一位有效編碼。其方法是使用 N位 狀態(tài)寄存器來對(duì) N個(gè)狀態(tài) 進(jìn)行編碼,每個(gè)狀態(tài)都有它獨(dú)立的寄存器位,并且在任意時(shí)候,其中只有一位有效,這篇文章主要介紹了機(jī)器學(xué)習(xí)數(shù)據(jù)預(yù)處理之獨(dú)熱One-Hot編碼及其代碼詳解,需要的朋友可以參考下2022-07-07本地部署Python?Flask并搭建web問答應(yīng)用程序框架實(shí)現(xiàn)遠(yuǎn)程訪問的操作方法
Flask是一個(gè)Python編寫的Web微框架,使用Python語言快速實(shí)現(xiàn)一個(gè)網(wǎng)站或Web服務(wù),本期教程我們使用Python Flask搭建一個(gè)web問答應(yīng)用程序框架,并結(jié)合cpolar內(nèi)網(wǎng)穿透工具將我們的應(yīng)用程序發(fā)布到公共網(wǎng)絡(luò)上,實(shí)現(xiàn)可多人遠(yuǎn)程進(jìn)入到該web應(yīng)用程序訪問,需要的朋友可以參考下2023-12-12