Z wyłączeniem katalogów w os.walk

148

Piszę skrypt, który schodzi do drzewa katalogów (używając os.walk ()), a następnie odwiedza każdy plik pasujący do określonego rozszerzenia. Ponieważ jednak niektóre drzewa katalogów, w których moje narzędzie będzie używane, zawierają również podkatalogi, które z kolei zawierają DUŻO bezużytecznych (na potrzeby tego skryptu) rzeczy, pomyślałem, że dodam opcję dla użytkownika do określenia lista katalogów do wykluczenia z przemierzania.

Jest to dość łatwe dzięki os.walk (). W końcu to do mnie należy decyzja, czy faktycznie chcę odwiedzić odpowiednie pliki / katalogi udostępnione przez os.walk (), czy po prostu je pominąć. Problem w tym, że jeśli mam np. Drzewo katalogów takie jak to:

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

i chcę wykluczyć uselessStuff i wszystkie jego elementy podrzędne, os.walk () nadal będzie schodzić do wszystkich (potencjalnie tysięcy) podkatalogów uselessStuff , co, rzecz jasna, bardzo spowalnia działanie. W idealnym świecie mógłbym powiedzieć os.walk (), aby nawet nie zawracał sobie głowy dawaniem kolejnych dzieci uselessStuff , ale o ile wiem, nie ma na to sposobu (prawda?).

Czy ktoś ma pomysł? Może istnieje biblioteka innej firmy, która udostępnia coś takiego?

antred
źródło

Odpowiedzi:

243

Modyfikacja dirs na miejscu będzie przycinać (kolejne) plików i katalogów odwiedzanych przez 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]

Z pomocy (os.walk):

Kiedy topdown jest prawdziwe, dzwoniący może modyfikować listę dirnames w miejscu (np. Poprzez przypisanie del lub slice), a spacer będzie się powtarzał tylko do podkatalogów, których nazwy pozostają w dirnames; można to wykorzystać do przycięcia wyszukiwania ...

unutbu
źródło
31
Dlaczego dirs[:] =?
ben
56
@ben: dirs[:] = valuemodyfikuje dirs w miejscu . Zmienia zawartość listy dirsbez zmiany kontenera. Jak help(os.walk)wspomniano, jest to potrzebne, jeśli chcesz wpłynąć na sposób os.walkprzechodzenia przez podkatalogi. ( dirs = valuepo prostu ponownie przypisuje (lub „wiąże”) zmienną dirsdo nowej listy, bez modyfikowania oryginału dirs.)
unutbu
6
Możesz również użyć filter():dirs[:] = list(filter(lambda x: not x in exclude, dirs))
NuclearPeon
2
@ p014k: Mógłbyś napisać własną funkcję generatora, która wywołuje os.walki daje root, dirs, filespo wykluczeniu .git(lub czegokolwiek innego) z dirs.
unutbu
3
@unutbu Po prostu informuję, że w jednym przypadku ta optymalizacja skróciła czas przejścia z ponad 100 sekund do około 2 sekund. To właśnie nazywam wartościową optymalizacją. : D
antred
7

... alternatywna forma doskonałej odpowiedzi @ unutbu, która brzmi trochę bardziej bezpośrednio, biorąc pod uwagę, że celem jest wykluczenie katalogów kosztem czasu O (n ** 2) vs O (n).

( list(dirs)Do poprawnego wykonania wymagane jest wykonanie kopii listy katalogów z )

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    [dirs.remove(d) for d in list(dirs) if d in exclude]
Dmitri
źródło
5
Jeśli chcesz być bardziej bezpośredni kosztem trochę pamięci, lepiej napisz dirs[:] = set(dirs) - exclude. Przynajmniej nadal jest \ $ O (n) \ $ i nie budujesz zrozumienia tylko dla jego skutków ubocznych ...
301_Moved_Permanently
3
To nie jest złe, ale moim zdaniem nie jest też idiomatycznym Pythonem.
Torsten Bronger
for d in list(dirs)jest trochę dziwne. dirsjest już listą. A to, co masz, nie jest tak naprawdę zrozumieniem listy. dirs.remove(d)nic nie zwraca, więc otrzymujesz listę pełną Nones. Zgadzam się z @Torsten.
seanahern