您好,登錄后才能下訂單哦!
Python中怎么解壓zip文件,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
首先是下面這些模擬對 zip 文件中文件實際操作的普通函數:
def _count_file(fn): with open(fn, 'rb') as f: return _count_file_object(f) def _count_file_object(f): # Note that this iterates on 'f'. # You *could* do 'return len(f.read())' # which would be faster but potentially memory # inefficient and unrealistic in terms of this # benchmark experiment. total = 0 for line in f: total += len(line) return total
這里是可能最簡單的另一個函數:
def f1(fn, dest): with open(fn, 'rb') as f: zf = zipfile.ZipFile(f) zf.extractall(dest) total = 0 for root, dirs, files in os.walk(dest): for file_ in files: fn = os.path.join(root, file_) total += _count_file(fn) return total
如果我更仔細地分析一下,我將會發現這個函數花費時間 40% 運行 extractall
,60% 的時間在遍歷各個文件并讀取其長度。
我的***步嘗試是使用線程。先創建一個 zipfile.ZipFile
的實例,展開其中的每個文件名,然后為每一個文件開始一個線程。每個線程都給它一個函數來做“實質工作”(在這個基準測試中,就是遍歷每個文件然后獲取它的名稱)。實際業務中的函數進行的工作是復雜的 S3、Redis 和 PostgreSQL 操作,但是在我的基準測試中我只需要制作一個可以找出文件長度的函數就好了。線程池函數:
def f2(fn, dest): def unzip_member(zf, member, dest): zf.extract(member, dest) fn = os.path.join(dest, member.filename) return _count_file(fn) with open(fn, 'rb') as f: zf = zipfile.ZipFile(f) futures = [] with concurrent.futures.ThreadPoolExecutor() as executor: for member in zf.infolist(): futures.append( executor.submit( unzip_member, zf, member, dest, ) ) total = 0 for future in concurrent.futures.as_completed(futures): total += future.result() return total
結果:加速 ~10%
所以可能是 GIL(LCTT 譯注:Global Interpreter Lock,一種全局鎖,CPython 中的一個概念)阻礙了我。最自然的想法是嘗試使用多線程在多個 CPU 上分配工作。但是這樣做有缺點,那就是你不能傳遞一個非可 pickle 序列化的對象(LCTT 譯注:意為只有可 pickle 序列化的對象可以被傳遞),所以你只能發送文件名到之后的函數中:
def unzip_member_f3(zip_filepath, filename, dest): with open(zip_filepath, 'rb') as f: zf = zipfile.ZipFile(f) zf.extract(filename, dest) fn = os.path.join(dest, filename) return _count_file(fn) def f3(fn, dest): with open(fn, 'rb') as f: zf = zipfile.ZipFile(f) futures = [] with concurrent.futures.ProcessPoolExecutor() as executor: for member in zf.infolist(): futures.append( executor.submit( unzip_member_f3, fn, member.filename, dest, ) ) total = 0 for future in concurrent.futures.as_completed(futures): total += future.result() return total
結果: 加速 ~300%
使用處理器池的問題是這樣需要存儲在磁盤上的原始 .zip
文件。所以為了在我的 web 服務器上使用這個解決方案,我首先得要將內存中的 zip 文件保存到磁盤,然后調用這個函數。這樣做的代價我不是很清楚但是應該不低。
好吧,再翻翻看又沒有損失。可能,解壓過程加速到足以彌補這樣做的損失了吧。
但是一定記住!這個優化取決于使用所有可用的 CPU。如果一些其它的 CPU 需要執行在 gunicorn
中的其它事務呢?這時,這些其它進程必須等待,直到有 CPU 可用。由于在這個服務器上有其他的事務正在進行,我不是很確定我想要在進程中接管所有其他 CPU。
一步一步地做這個任務的這個過程感覺挺好的。你被限制在一個 CPU 上但是表現仍然特別好。同樣地,一定要看看在f1
和 f2
兩段代碼之間的不同之處!利用 concurrent.futures
池類你可以獲取到允許使用的 CPU 的個數,但是這樣做同樣給人感覺不是很好。如果你在虛擬環境中獲取的個數是錯的呢?或者可用的個數太低以致無法從負載分配獲取好處并且現在你僅僅是為了移動負載而支付營運開支呢?
我將會繼續使用 zipfile.ZipFile(file_buffer).extractall(temp_dir)
。這個工作這樣做已經足夠好了。
我使用一個 c5.4xlarge
EC2 服務器來進行我的基準測試。文件可以從此處下載:
wget https://www.peterbe.com/unzip-in-parallel/hack.unzip-in-parallel.pywget https://www.peterbe.com/unzip-in-parallel/symbols-2017-11-27T14_15_30.zip
這里的 .zip
文件有 34MB。和在服務器上的相比已經小了很多。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。