Виключення каталогів у os.walk


148

Я пишу сценарій, який спускається в дерево каталогів (використовуючи os.walk ()), а потім відвідую кожен файл, який відповідає певному розширенню файлу. Однак, оскільки деякі дерева директорій, на яких буде використовуватися мій інструмент, також містять підкаталоги, які, у свою чергу, містять a БАГАТО непотрібних (для цілей даного сценарію) речі, я вирішив додати можливість для користувача , щоб вказати список каталогів, які потрібно виключити з обходу.

Це досить просто за допомогою os.walk (). Зрештою, я вирішую, чи дійсно я хочу відвідати відповідні файли / файли, видані os.walk (), або просто пропустити їх. Проблема полягає в тому, що якщо у мене є, наприклад, дерево каталогів таким:

root--
     |
     --- dirA
     |
     --- dirB
     |
     --- uselessStuff --
                       |
                       --- moreJunk
                       |
                       --- yetMoreJunk

і я хочу виключити безполезнийStuff та всіх його дітей, os.walk () все одно зійде у всі (потенційно тисячі) підкаталогів безполезнихStuff , що, не треба говорити, значно уповільнює справи. В ідеальному світі, я можу сказати os.walk (), що навіть не турбує народжувати більше дітей непотрібнихStuff , але, наскільки мені відомо, немає способу зробити це (чи є?).

Хтось має ідею? Можливо, є стороння бібліотека, яка надає щось подібне?

Відповіді:


243

Змінення dirs на місці обріже (наступні) файли та каталоги, які відвідує os.walk:

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    dirs[:] = [d for d in dirs if d not in exclude]

З довідки (os.walk):

Якщо вікно вгорі відповідає дійсності, абонент може змінити набір списків прізвищ на місці (наприклад, через присвоєння del або фрагмента), і прогулянка буде повторюватися лише у підкаталогах, імена яких залишаються у dirname; це можна використати для обрізки пошуку ...


31
Чому dirs[:] =?
ben

56
@ben: dirs[:] = valueзмінює dirs на місці . Він змінює вміст списку, dirsне змінюючи контейнер. Як help(os.walk)згадується, це потрібно, якщо ви хочете вплинути на спосіб os.walkпереходу підкаталогів. ( dirs = valueпросто переназначає (або "прив'язує") змінну dirsдо нового списку, не змінюючи оригінал dirs.)
unutbu

6
Ви також можете скористатися filter():dirs[:] = list(filter(lambda x: not x in exclude, dirs))
NuclearPeon

2
@ p014k: Ви можете написати власну генераторну функцію, яка викликає os.walkта видає, root, dirs, filesпісля виключення .git(або будь-якого іншого) dirs.
unutbu

3
@unutbu Повідомляючи, що в одному випадку ця оптимізація скоротила час переходу з більш ніж 100 секунд до приблизно 2 секунд. Це те, що я називаю гідною оптимізацією. : D
антред

7

... альтернативна форма відмінного відповіді @ unutbu, яка читається трохи безпосередньо, враховуючи, що наміром є виключення каталогів, ціною часу O (n ** 2) проти O (n).

(Для list(dirs)правильного виконання потрібна копія списку "dirs" )

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    [dirs.remove(d) for d in list(dirs) if d in exclude]

5
Якщо ви хочете бути більш прямим ціною деякої пам’яті, то краще напишіть dirs[:] = set(dirs) - exclude. Принаймні, це все ще \ $ O (n) \ $, і ви не будуєте розуміння лише для його побічних ефектів ...
301_Moved_Permanently

3
Насправді це не дуже погано, але не є ідіоматичним Python.
Торстен Бронгер

for d in list(dirs)трохи дивно. dirsце вже список. І те, що у вас є, насправді не є розумінням списку. dirs.remove(d)не повертає нічого, тому ви закінчуєте список, заповнений Nones. Я згоден з @Torsten.
seanahern
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.