Recursion Recursion Recursion --- 我怎样才能提高性能? (Python存档递归提取)

Posted

技术标签:

【中文标题】Recursion Recursion Recursion --- 我怎样才能提高性能? (Python存档递归提取)【英文标题】:Recursion Recursion Recursion --- How can i Improve Performance? (Python Archive Recursive Extraction) 【发布时间】:2010-07-24 04:35:23 【问题描述】:

我正在尝试开发递归提取器。问题是,它递归太多(每次找到存档类型)并且性能受到影响。

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

我的想法 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)

【问题讨论】:

【参考方案1】:

如果您想将存档文件提取到它们所在路径“上方”的路径,os.walk 本身(在其正常的自上而下操作中)无法帮助您(因为当您将档案提取到某个目录 x 中时,os.walk 可能(尽管不一定)已经考虑过目录 x ——因此只有让 os.walk 一遍又一遍地查看整个路径,您才能获得所有内容) .除了,我很惊讶你的代码会终止,因为归档类型的文件应该不断被发现和提取——我看不出什么可以终止递归。 (为了解决这个问题,保留一组您已经提取的归档类型文件的所有路径就足够了,以避免再次遇到它们时再次考虑它们。

无论如何,到目前为止,最好的架构是如果arcExtract 返回它已提取的所有文件的列表(特别是它们的目标路径)——那么您可以简单地继续扩展包含所有这些提取文件的列表在os.walk 循环期间(无递归),然后继续在列表上循环(无需继续询问操作系统有关文件和目录的信息,也节省了该操作的大量时间)并生成一个新的类似列表。没有递归,没有工作冗余。我想readpst7z 能够以某种文本形式提供这样的列表(可能在他们的标准输出或错误上,你目前只是显示但不处理),你可以解析它以使其成为一个列表...?

【讨论】:

是的,你可能想知道它是如何停止的,但它会因为这个而停止:在 arcExtract: if unlink==True: os.unlink(file_path) 取消归档后删除档案。需要递归,因为有 Archives , Inside Archives , Imagine tar.gz 和 tar.bz2 ,都需要递归提取。 PST 文件还包含带有存档的附件,它们都需要被提取。 您建议的架构是可行的。我会试试。唯一的问题是 readpst 不输出提取的文件夹路径。我可以在 -o"Path_To_Extract" 选项上强制使用它,但它需要先检查现有文件夹名称以防止冲突。 @V3ss,感谢您阐明它停止的原因(即,由于取消归档后的删除)并看到适当的迭代(如果您可以获得提取的文件的路径)确实可以消除需要对于递归(尽管我确实已经考虑过arch-inside-arch问题)——祝readpst做你需要的事情(听起来像是superuser.com的问题;-)。【参考方案2】:

您可以简化您的 extractRecursive 方法以使用应使用的 os.walkos.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"

【讨论】:

需要递归调用,因为这是多级存档提取。例如,假设我们有 50 个存档,其中可能包含 tar.gz 和 tar.bz2 等存档,这是 bz2 存档中的 tar 存档,该功能需要先从 bz2 存档中提取 tar,然后再次提取 tar 存档。并且 7z 是我得到的唯一选择(它不会像 tar 那样自动执行)。它的作用是提取 bz2 ,然后递归到循环中,以找到它再次提取的 tar 文件。这需要额外的性能,尤其是当文件位于列表中间时。 @V3ss0n:我明白了。在这种情况下,您有 2 个选项。 1. Alex Martelli 所建议的,一个干净但可能更困难的解决方案。 2. 一个 1-n 运行循环,您一直运行直到找不到新档案。这需要您跟踪提取的档案,但实施起来相当容易。 WoLpH ,您能否详细说明您的第二个想法? @V3ss0n:我添加了一个示例。它不是那么漂亮,但它应该可以在没有无限递归的情况下工作。

以上是关于Recursion Recursion Recursion --- 我怎样才能提高性能? (Python存档递归提取)的主要内容,如果未能解决你的问题,请参考以下文章

JPA出现recursion 死循环导致栈内存溢出问题 Could not write JSON: Infinite recursion (StackOverflowError)

递归(recursion)方法

Lintcode371 Print Numbers by Recursion solution 题解

函数式编程公理

Divison and Recursion-MergeSort

为啥我的 Mandelbrot-recursion 只打印废话?