Рекурсия Рекурсия Рекурсия --- Как я могу улучшить производительность?(Python Archive Recursive Extraction) - PullRequest
0 голосов
/ 24 июля 2010

Я пытаюсь разработать рекурсивный экстрактор. Проблема в том, что он слишком много повторяется (Evertime нашел тип архива) и снижает производительность.

Так, как я могу улучшить код ниже?

Моя идея 1:

Сначала получите 'Dict' директорий вместе с типами файлов. Типы файлов в качестве ключей. Извлеките типы файлов. Когда архив найден, извлеките только этот. Затем снова восстановите Dict в архиве.

Моя идея 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 голос
/ 24 июля 2010

Если, как выяснилось, вы хотите извлечь архивные файлы по пути «выше» того, в котором они находятся, os.walk как таковой (в своей обычной нисходящей операции) вам не поможет (потому чтоКогда вы извлекаете архив в определенный каталог x, os.walk, вероятно, может, хотя и не обязательно, уже рассматривать каталог x - поэтому, только если os.walk просматривает весь путь снова и снова, вы можете получить все содержимое),Кроме того, я удивлен, что ваш код когда-либо завершается, поскольку файлы архивного типа должны постоянно обнаруживаться и извлекаться - я не вижу, что может прекратить рекурсию.(Чтобы решить, что было бы достаточно сохранить набор всех путей к файлам архивного типа, которые вы уже извлекли, чтобы не рассматривать их снова, когда вы встретите их снова).

Безусловно, лучшая архитектура,в любом случае, было бы, если бы arcExtract должен был вернуть список всех извлеченных файлов (в частности, их пути назначения) - тогда вы могли бы просто продолжать расширять список со всеми этими извлеченными файлами во время цикла os.walk (без рекурсии)), а затем продолжайте цикл только по списку (не нужно спрашивать ОС о файлах и каталогах, экономя много времени на этой операции) и создавать новый аналогичный список.Нет рекурсии, нет избыточности работы.Я полагаю, что readpst и 7z могут предоставлять такие списки (возможно, при их стандартном выводе или ошибке, которую вы в настоящее время просто отображаете, но не обрабатываете) в некоторой текстовой форме, которую вы можете проанализировать, чтобы превратить ее в список....

1 голос
/ 24 июля 2010

Вы можете упростить свой метод 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"
...