递归递归递归 --- 如何提升性能?(Python归档递归提取)
我正在尝试开发一个递归提取器。问题是,它递归得太多了(每次找到一个压缩文件类型时),这导致性能下降。
那么,我该如何改进下面的代码呢?
我的想法1:
先获取一个包含目录和文件类型的字典。文件类型作为键。提取出文件类型。当找到一个压缩文件时,只提取那个文件。然后再重新生成压缩文件的字典。
我的想法2:
os.walk返回的是一个生成器。那么,我可以用生成器做些什么呢?我对生成器还不太熟悉。
这是当前的代码:
import os, magic
m = magic.open( magic.MAGIC_NONE )
m.load()
archive_type = [ 'gzip compressed data',
'7-zip archive data',
'Zip archive data',
'bzip2 compressed data',
'tar archive',
'POSIX tar archive',
'POSIX tar archive (GNU)',
'RAR archive data',
'Microsoft Outlook email folder (>=2003)',
'Microsoft Outlook email folder']
def extractRecursive( path ,archives):
i=0
for dirpath, dirnames, filenames in os.walk( path ):
for f in filenames:
fp = os.path.join( dirpath, f )
i+=1
print i
file_type = m.file( fp ).split( "," )[0]
if file_type in archives:
arcExtract(fp,file_type,path,True)
extractRecursive(path,archives)
return "Done"
def arcExtract(file_path,file_type,extracted_path="/home/v3ss/Downloads/extracted",unlink=False):
import subprocess,shlex
if file_type in pst_types:
cmd = "readpst -o '%s' -S '%s'" % (extracted_path,file_path)
else:
cmd = "7z -y -r -o%s x '%s'" % (extracted_path,file_path)
print cmd
args= shlex.split(cmd)
print args
try:
sp = subprocess.Popen( args, shell = False, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
out, err = sp.communicate()
print out, err
ret = sp.returncode
except OSError:
print "Error no %s Message %s" % (OSError.errno,OSError.message)
pass
if ret == 0:
if unlink==True:
os.unlink(file_path)
return "OK!"
else:
return "Failed"
if __name__ == '__main__':
extractRecursive( 'Path/To/Archives' ,archive_type)
2 个回答
你可以把你的 extractRecursive
方法简化一下,使用 os.walk
,这样用才对。其实 os.walk
已经可以读取所有的子目录,所以你不需要再用递归了。
只要去掉递归调用,这样就可以正常工作了 :)
def extractRecursive(path, archives, extracted_archives=None):
i = 0
if not extracted_archives:
extracted_archives = set()
for dirpath, dirnames, filenames in os.walk(path):
for f in filenames:
fp = os.path.join(dirpath, f)
i += 1
print i
file_type = m.file(fp).split(',')[0]
if file_type in archives and fp not in extracted_archives:
extracted_archives.add(fp)
extracted_in.add(dirpath)
arcExtract(fp, file_type, path, True)
for path in extracted_in:
extractRecursive(path, archives, extracted_archives)
return "Done"
如果你想把压缩文件解压到它们所在路径的“上层”目录,os.walk
这个工具在正常的自上而下的操作中是帮不了你的。因为当你把一个压缩文件解压到某个目录x时,os.walk
可能已经处理过这个目录x了(虽然不一定)。所以,只有让os.walk
不断重复查看整个路径,才能获取所有内容。更让我惊讶的是,你的代码居然能结束,因为压缩文件应该会一直被找到并解压,我看不出有什么能让递归停止的办法。(要解决这个问题,只需要记录下你已经解压过的所有压缩文件的路径,这样下次遇到它们时就可以避免重复处理。)
不过,最好的方法是让arcExtract
返回一个它解压出来的所有文件的列表(具体是它们的目标路径)。这样你就可以在os.walk
循环中不断把这些解压出来的文件添加到一个列表里(不需要递归),然后只需在这个列表上继续循环(不需要再询问操作系统关于文件和目录的信息,这样也能节省很多时间),并生成一个新的类似列表。没有递归,也没有重复的工作。我想readpst
和7z
应该能提供这样的列表(也许是在它们的标准输出或错误输出中,你现在只是显示这些信息,但没有处理),以某种文本形式供你解析,变成一个列表……?