摘要:圖片下載屬于操作,比較耗時(shí),基于此,可以利用中的多線程將其實(shí)現(xiàn)。更多精彩滾雪球?qū)W完結(jié)滾雪球?qū)W第二輪完結(jié)滾雪球?qū)W第三輪滾雪球?qū)W番外篇完結(jié)
在 python 編碼過(guò)程中,有時(shí)存在這樣的一個(gè)需求,同時(shí)下載 N 張圖片,并且要快。
一般這樣的需求,只需要編寫(xiě)一個(gè) for 循環(huán)即可實(shí)現(xiàn),但是加上 快 這個(gè)要求,就不好實(shí)現(xiàn)了。
圖片下載屬于 I/O 操作,比較耗時(shí),基于此,可以利用 python 中的多線程將其實(shí)現(xiàn)。
為了不讓大家學(xué)的太困倦,特意找來(lái) 6 張美麗的圖片,本次學(xué)習(xí)將圍繞這幾張圖片進(jìn)行。
https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpghttps://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg
使用 for 循環(huán),同步代碼如下所示:
import timeimport requestsurls = [ "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]# 文件保存路徑SAVE_DIR = "./928/"def save_img(url): res = requests.get(url) with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f: f.write(res.content)if __name__ == "__main__": # 下載開(kāi)始時(shí)間 start_time = time.perf_counter() for url in urls: save_img(url) print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time) # 下載 6 張圖片消耗時(shí)間為: 1.911142665
接下來(lái)使用 concurrent.futures 模塊
實(shí)現(xiàn)對(duì) 6 張圖片的下載,這個(gè)模塊實(shí)現(xiàn)了 ThreadPoolExecutor
類(lèi)和 ProcessPoolExecutor
類(lèi),都繼承自Executor
,分別被用來(lái)創(chuàng)建 線程池 和 進(jìn)程池,接受 max_workers
參數(shù),代表創(chuàng)建的線程數(shù)或者進(jìn)程數(shù)。
這兩個(gè)類(lèi)可以在不同的線程或進(jìn)程中執(zhí)行 可調(diào)用對(duì)象,ProcessPoolExecutor
的 max_workers
參數(shù)可以為空,程序會(huì)自動(dòng)創(chuàng)建與電腦 CPU
數(shù)目相同的進(jìn)程數(shù)。
使用 ThreadPoolExecutor
實(shí)現(xiàn)多線程下載
import timeimport requestsfrom concurrent import futuresMAX_WORKERS = 20 # 最大線程數(shù)SAVE_DIR = "./928/" # 文件保存路徑urls = [ "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]def save_img(url): res = requests.get(url) with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f: f.write(res.content)if __name__ == "__main__": start_time = time.perf_counter() # 下載開(kāi)始時(shí)間 with futures.ThreadPoolExecutor(MAX_WORKERS) as executor: res = executor.map(save_img, urls) # executor.map() 方法會(huì)返回一個(gè)生成器,后續(xù)代碼可以迭代獲取每個(gè)線程的執(zhí)行結(jié)果 print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time) # 下載 6 張圖片消耗時(shí)間為: 0.415939759
當(dāng)使用多線程代碼之后,時(shí)間從單線程的 1.9s
變?yōu)榱硕嗑€程的 0.4s
,能看到效率的提升。
在上述多線程代碼中,使用了 concurrent
庫(kù)中的 future
對(duì)象,該對(duì)象是 Future
類(lèi)的對(duì)象,它的實(shí)力表示可能已經(jīng)完成或尚未完成的 延遲計(jì)算,該類(lèi)具備 done()
方法,返回調(diào)用對(duì)象是否已經(jīng)執(zhí)行,有該方法的同時(shí)還具備一個(gè) add_done_callback()
方法,表示調(diào)用對(duì)象執(zhí)行完畢的回調(diào)函數(shù)。
from concurrent.futures import ThreadPoolExecutordef print_name(): return "橡皮擦"def say_hello(obj): """可調(diào)用對(duì)象執(zhí)行完畢,綁定的回調(diào)函數(shù)""" w_name = obj.result() s = w_name + "你好" print(s) return swith ThreadPoolExecutor(1) as executor: executor.submit(print_name).add_done_callback(say_hello)
在上述代碼中用到了如下知識(shí)點(diǎn):
executor.map()
:該方法類(lèi)似 map
函數(shù),原型為 map(func, *iterables, timeout=None, chunksize=1)
,異步執(zhí)行 func
,并支持多次并發(fā)調(diào)用;executor.submit()
:方法原型為 submit(fn, *args, **kwargs)
,安排可調(diào)用對(duì)象 fn
以 fn(*args, **kwargs)
的形式執(zhí)行,并返回 Future
對(duì)象來(lái)表示它的執(zhí)行結(jié)果,該方法只能進(jìn)行單個(gè)任務(wù),如果需要并發(fā)多個(gè)任務(wù),需要使用 map
或者 as_completed
;future對(duì)象.result()
:返回調(diào)用返回的值,有個(gè)等待時(shí)間參數(shù) timeout
可以設(shè)置;future對(duì)象add_done_callback()
:該方法中綁定的回調(diào)函數(shù)在 future
取消或者完成后運(yùn)行,表示 future
本身as_completed() 方法
該方法參數(shù)是一個(gè) Future
列表,返回值是一個(gè) Future
組成的生成器,在調(diào)用 as_completed()
方法時(shí)不會(huì)阻塞,只有當(dāng)對(duì)迭代器進(jìn)行循環(huán)時(shí),每調(diào)用一次 next()
方法,如果當(dāng)前 Future
對(duì)象還未完成,則會(huì)阻塞。
修改下載 6 張圖片的代碼,使用 as_completed()
進(jìn)行實(shí)現(xiàn),最后得到的時(shí)間優(yōu)于單線程,但如果對(duì)結(jié)果進(jìn)行迭代,調(diào)用 result()
方法,則時(shí)間會(huì)加長(zhǎng)。
import timeimport requestsfrom concurrent.futures import ThreadPoolExecutor, as_completedMAX_WORKERS = 20 # 最大線程數(shù)SAVE_DIR = "./928/" # 文件保存路徑urls = [ "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]def save_img(url): res = requests.get(url) with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f: f.write(res.content)if __name__ == "__main__": start_time = time.perf_counter() # 下載開(kāi)始時(shí)間 with ThreadPoolExecutor(MAX_WORKERS) as executor: tasks = [executor.submit(save_img, url) for url in urls] # 去除下部分代碼,時(shí)間基本與 map 一致。 for future in as_completed(tasks): print(future.result()) print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time) # 下載 6 張圖片消耗時(shí)間為: 0.840261401
wait
方法
wait
方法可以讓主線程阻塞,直到滿足設(shè)定的要求,該要求為 return_when
參數(shù),其值有 ALL_COMPLETED
,FIRST_COMPLETED
,FIRST_EXCEPTION
。
import timeimport requestsfrom concurrent.futures import ThreadPoolExecutor, as_completed, wait, ALL_COMPLETED, FIRST_COMPLETEDMAX_WORKERS = 20 # 最大線程數(shù)SAVE_DIR = "./928/" # 文件保存路徑urls = [ "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-004.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-012.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-013.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-016.jpg", "https://img-pre.ivsky.com/img/tupian/pre/202102/21/oumei_meinv-010.jpg"]def save_img(url): res = requests.get(url) with open(F"{SAVE_DIR}{time.time()}.jpg", "wb+") as f: f.write(res.content)if __name__ == "__main__": start_time = time.perf_counter() # 下載開(kāi)始時(shí)間 with ThreadPoolExecutor(MAX_WORKERS) as executor: tasks = [executor.submit(save_img, url) for url in urls] wait(tasks, return_when=ALL_COMPLETED) print("程序運(yùn)行完畢") print("下載 6 張圖片消耗時(shí)間為:", time.perf_counter() - start_time) # 下載 6 張圖片消耗時(shí)間為: 0.48876672
最后一句:ProcessPoolExecutor
的用法與 ThreadPoolExecutor
的用法基本一致,所以可以互通。
以上內(nèi)容就是本文的全部?jī)?nèi)容,希望對(duì)學(xué)習(xí)路上的你有所幫助~
今天是持續(xù)寫(xiě)作的第 235 / 365 天。
期待 關(guān)注,點(diǎn)贊、評(píng)論、收藏。
更多精彩
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/122205.html
摘要:的安裝博客補(bǔ)充知識(shí)年最新安裝教程,滾雪球?qū)W第四季。操作操作數(shù)據(jù)庫(kù)一般被程序員成為操作增刪改查,其中各個(gè)字符分別代表新增,讀取,更新,刪除。可以返回受影響行數(shù),可以直接通過(guò)該值判斷是否修改成功。 ...
摘要:看起來(lái)好像是廢話,它還有一個(gè)補(bǔ)充的說(shuō)明,在函數(shù)式編程中要避免狀態(tài)變化和使用可變對(duì)象。函數(shù)式編程的特點(diǎn)在中,函數(shù)即對(duì)象,例如聲明一個(gè)函數(shù)之后,你可以調(diào)用其屬性。 ...
摘要:在流程控制中,你將同步學(xué)到關(guān)系運(yùn)算符與邏輯運(yùn)算符。關(guān)系運(yùn)算符在中關(guān)系運(yùn)算符其實(shí)就是比大小的概念,所以要學(xué)習(xí)的就是大于小于等于等內(nèi)容。邏輯運(yùn)算符邏輯運(yùn)算符在中有個(gè),分別是。含有邏輯運(yùn)算符的式子,最終返回的結(jié)果也是布爾值。 滾雪球?qū)W Python,目標(biāo)就是讓 Python 學(xué)起來(lái)之后,越滾越大。三、無(wú)轉(zhuǎn)折不編程如果...
摘要:時(shí)間永遠(yuǎn)都過(guò)得那么快,一晃從年注冊(cè),到現(xiàn)在已經(jīng)過(guò)去了年那些被我藏在收藏夾吃灰的文章,已經(jīng)太多了,是時(shí)候把他們整理一下了。那是因?yàn)槭詹貖A太亂,橡皮擦給設(shè)置私密了,不收拾不好看呀。 ...
摘要:返回值,非必須,返回多個(gè)值使用逗號(hào)分隔即可。注意第一行末尾的分號(hào)無(wú)參數(shù)無(wú)返回值的函數(shù)該內(nèi)容將演示函數(shù)的使用便捷性。函數(shù)的返回值可以賦值給一個(gè)變量,通過(guò)打印該變量,即可知道返回的具體內(nèi)容。先學(xué)習(xí)一下局部變量與全局變量。 Python 學(xué)習(xí)的第一個(gè)難關(guān) -- 函數(shù),這個(gè)地方學(xué)會(huì)的人覺(jué)得沒(méi)有啥,沒(méi)學(xué)過(guò)的學(xué)的時(shí)候迷迷瞪...
閱讀 1055·2021-10-11 10:59
閱讀 3607·2021-09-26 09:55
閱讀 900·2019-08-30 15:55
閱讀 2655·2019-08-30 15:44
閱讀 439·2019-08-30 14:06
閱讀 687·2019-08-30 11:26
閱讀 3344·2019-08-30 10:49
閱讀 2492·2019-08-29 12:53