递归递归递归 --- 如何提升性能?(Python归档递归提取)

0 投票
2 回答
828 浏览
提问于 2025-04-16 01:45

我正在尝试开发一个递归提取器。问题是,它递归得太多了(每次找到一个压缩文件类型时),这导致性能下降。

那么,我该如何改进下面的代码呢?

我的想法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 个回答

1

你可以把你的 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"
1

如果你想把压缩文件解压到它们所在路径的“上层”目录,os.walk这个工具在正常的自上而下的操作中是帮不了你的。因为当你把一个压缩文件解压到某个目录x时,os.walk可能已经处理过这个目录x了(虽然不一定)。所以,只有让os.walk不断重复查看整个路径,才能获取所有内容。更让我惊讶的是,你的代码居然能结束,因为压缩文件应该会一直被找到并解压,我看不出有什么能让递归停止的办法。(要解决这个问题,只需要记录下你已经解压过的所有压缩文件的路径,这样下次遇到它们时就可以避免重复处理。)

不过,最好的方法是让arcExtract返回一个它解压出来的所有文件的列表(具体是它们的目标路径)。这样你就可以在os.walk循环中不断把这些解压出来的文件添加到一个列表里(不需要递归),然后只需在这个列表上继续循环(不需要再询问操作系统关于文件和目录的信息,这样也能节省很多时间),并生成一个新的类似列表。没有递归,也没有重复的工作。我想readpst7z应该能提供这样的列表(也许是在它们的标准输出或错误输出中,你现在只是显示这些信息,但没有处理),以某种文本形式供你解析,变成一个列表……?

撰写回答