#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 есть anos.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
папку.