Как я могу ускорить этот обход каталога?

#python #os.walk #scandir

#python #os.walk #scandir

Вопрос:

Я пытаюсь написать функцию, которая находит каталог, просматривая дерево каталогов с os.walk() помощью ниже. На моей машине это занимает 15 секунд.

 for dir_path, dir_names, filenames in os.walk(os.path.expanduser('~')):
    for dir_name in dir_names:
        if dir_name == 'some_dir':
            path = os.path.join(dir_path, dir_name)
            print(path)
  

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

 for dir_path, dir_names, filenames in os.walk(os.path.expanduser('~')):
    with os.scandir(dir_path) as entries:
        for entry in entries:
            if entry.name.endswith('some_dir') and entry.is_dir():
                print(entry.path)
  

Как я могу ускорить это?

Комментарии:

1. os.scandir() необходимо использовать вместо os.walk() , если вы хотите, чтобы у него был шанс сделать что-нибудь быстрее. Использование этого в дополнение означает, что вы загружаете имена из операционной системы дважды — один раз из os.walk() с обычной производительностью, а другой раз из os.scandir() .

2. @CharlesDuffy, насколько я знаю, os.scandir() является заменой os.listdir() , поэтому он возвращает итератор по элементам каталога, но не рекурсивно. Поэтому, если его замена os.walk() вернет только элементы в home папке — , но не в подкаталогах.

3. Правильно, поэтому вам нужно будет вызывать его несколько раз, по одному разу для каждого каталога, в который выполняется рекурсия — точно так os.walk() же, как это происходит под капотом. В то время как, если в вашей версии Python есть an os.walk() , который использует scandir вместо listdir уже готового, там может быть не так много дополнительного запаса.

4. Выполнение полного сканирования каталога всегда происходит медленно — вот почему люди обычно предпочитают использовать locate вместо find там, где это возможно. И find выполняет множество сложных низкоуровневых оптимизаций под капотом.

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

Ответ №1:

Одним из предложений было бы заменить for dir_name in dir_names часть:

 for dir_path, dir_names, filenames in os.walk(os.path.expanduser('~'):
    if 'some_dir' in dir_names:
        path = os.path.join(dir_path, 'some_dir')
        print(path)
  

Я не знаю, много ли у вас подкаталогов, но в зависимости от этого это уже должно ускорить код.

Кроме того, я бы предложил прокомментировать

os.scandir() Функция предпочтительнее os.listdir , когда вам нужна дополнительная информация о типе файла, но поскольку dir_names содержит только каталоги вложенной папки, это дополнительные накладные расходы, которые вы создаете, вызывая эту функцию, следовательно, она намного медленнее, чем исходный код.

Если вы используете версию python >=3.5 , то os.walk() уже вызываете os.scandir() под капотом. Как уже упоминал Чарльз Даффи, os.scandir() рекурсивный вызов по отдельности, скорее всего, будет не намного быстрее.

Комментарии:

1. Вы правы, это for dir_name in dir_names было излишним, но удаление этой строки не показало никаких улучшений скорости.

2. Тогда я бы посоветовал вам прочитать комментарии Чарльза. Если, например 'some_dir' , каталог расположен только в одном из ['Documents', 'Downloads', 'data'] , то может быть намного быстрее использовать только os.walk() их, а не всю home папку.