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