今天小編給大家分享一下Python如何解壓zip文件的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站建設(shè)、成都網(wǎng)站制作、壽縣網(wǎng)絡(luò)推廣、重慶小程序開發(fā)公司、壽縣網(wǎng)絡(luò)營(yíng)銷、壽縣企業(yè)策劃、壽縣品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供壽縣建站搭建服務(wù),24小時(shí)服務(wù)熱線:13518219792,官方網(wǎng)址:www.cdcxhl.com
首先是下面這些模擬對(duì) zip 文件中文件實(shí)際操作的普通函數(shù):
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
這里是可能最簡(jiǎn)單的另一個(gè)函數(shù):
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
如果我更仔細(xì)地分析一下,我將會(huì)發(fā)現(xiàn)這個(gè)函數(shù)花費(fèi)時(shí)間 40% 運(yùn)行 extractall
,60% 的時(shí)間在遍歷各個(gè)文件并讀取其長(zhǎng)度。
我的***步嘗試是使用線程。先創(chuàng)建一個(gè) zipfile.ZipFile
的實(shí)例,展開其中的每個(gè)文件名,然后為每一個(gè)文件開始一個(gè)線程。每個(gè)線程都給它一個(gè)函數(shù)來做“實(shí)質(zhì)工作”(在這個(gè)基準(zhǔn)測(cè)試中,就是遍歷每個(gè)文件然后獲取它的名稱)。實(shí)際業(yè)務(wù)中的函數(shù)進(jìn)行的工作是復(fù)雜的 S3、redis 和 PostgreSQL 操作,但是在我的基準(zhǔn)測(cè)試中我只需要制作一個(gè)可以找出文件長(zhǎng)度的函數(shù)就好了。線程池函數(shù):
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
結(jié)果:加速 ~10%
所以可能是 GIL(LCTT 譯注:Global Interpreter Lock,一種全局鎖,CPython 中的一個(gè)概念)阻礙了我。最自然的想法是嘗試使用多線程在多個(gè) CPU 上分配工作。但是這樣做有缺點(diǎn),那就是你不能傳遞一個(gè)非可 pickle 序列化的對(duì)象(LCTT 譯注:意為只有可 pickle 序列化的對(duì)象可以被傳遞),所以你只能發(fā)送文件名到之后的函數(shù)中:
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
結(jié)果: 加速 ~300%
使用處理器池的問題是這樣需要存儲(chǔ)在磁盤上的原始 .zip
文件。所以為了在我的 web 服務(wù)器上使用這個(gè)解決方案,我首先得要將內(nèi)存中的 zip 文件保存到磁盤,然后調(diào)用這個(gè)函數(shù)。這樣做的代價(jià)我不是很清楚但是應(yīng)該不低。
好吧,再翻翻看又沒有損失??赡?,解壓過程加速到足以彌補(bǔ)這樣做的損失了吧。
但是一定記??!這個(gè)優(yōu)化取決于使用所有可用的 CPU。如果一些其它的 CPU 需要執(zhí)行在 gunicorn
中的其它事務(wù)呢?這時(shí),這些其它進(jìn)程必須等待,直到有 CPU 可用。由于在這個(gè)服務(wù)器上有其他的事務(wù)正在進(jìn)行,我不是很確定我想要在進(jìn)程中接管所有其他 CPU。
一步一步地做這個(gè)任務(wù)的這個(gè)過程感覺挺好的。你被限制在一個(gè) CPU 上但是表現(xiàn)仍然特別好。同樣地,一定要看看在f1
和 f2
兩段代碼之間的不同之處!利用 concurrent.futures
池類你可以獲取到允許使用的 CPU 的個(gè)數(shù),但是這樣做同樣給人感覺不是很好。如果你在虛擬環(huán)境中獲取的個(gè)數(shù)是錯(cuò)的呢?或者可用的個(gè)數(shù)太低以致無法從負(fù)載分配獲取好處并且現(xiàn)在你僅僅是為了移動(dòng)負(fù)載而支付營(yíng)運(yùn)開支呢?
我將會(huì)繼續(xù)使用 zipfile.ZipFile(file_buffer).extractall(temp_dir)
。這個(gè)工作這樣做已經(jīng)足夠好了。
我使用一個(gè) c5.4xlarge
EC2 服務(wù)器來進(jìn)行我的基準(zhǔn)測(cè)試。文件可以從此處下載:
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。和在服務(wù)器上的相比已經(jīng)小了很多。
以上就是“Python如何解壓zip文件”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。