Przeprowadziłem testy szybkości różnych funkcji, aby zwrócić pełną ścieżkę do wszystkich bieżących podkatalogów.
tl; dr:
Zawsze używaj scandir:
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
Bonus: dzięki scandirmożesz także po prostu uzyskać nazwy folderów tylko za pomocą f.namezamiast f.path.
Ta (podobnie jak wszystkie inne funkcje poniżej) nie będzie używać naturalnego sortowania . Oznacza to, że wyniki będą sortowane w następujący sposób: 1, 10, 2. Aby uzyskać naturalne sortowanie (1, 2, 10), spójrz na https://stackoverflow.com/a/48030307/2441026
Wyniki :
scandirto: 3x szybciej niż walk, 32x szybciej niż listdir(z filtrem), 35x szybciej niż Pathlibi 36x szybciej niż listdiri 37x (!) Szybciej niż glob.
Testowane z W7x64, Python 3.8.1. Folder z 440 podfolderami.
Jeśli zastanawiasz się, czy listdirmożna by przyspieszyć, nie wykonując dwukrotnie os.path.join (), tak, ale różnica w zasadzie nie istnieje.
Kod:
import os
import pathlib
import timeit
import glob
path = r"<example_path>"def a():
list_subfolders_with_paths =[f.path for f in os.scandir(path)if f.is_dir()]# print(len(list_subfolders_with_paths))def b():
list_subfolders_with_paths =[os.path.join(path, f)for f in os.listdir(path)if os.path.isdir(os.path.join(path, f))]# print(len(list_subfolders_with_paths))def c():
list_subfolders_with_paths =[]for root, dirs, files in os.walk(path):for dir in dirs:
list_subfolders_with_paths.append( os.path.join(root, dir))break# print(len(list_subfolders_with_paths))def d():
list_subfolders_with_paths = glob.glob(path +'/*/')# print(len(list_subfolders_with_paths))def e():
list_subfolders_with_paths = list(filter(os.path.isdir,[os.path.join(path, f)for f in os.listdir(path)]))# print(len(list(list_subfolders_with_paths)))def f():
p = pathlib.Path(path)
list_subfolders_with_paths =[x for x in p.iterdir()if x.is_dir()]# print(len(list_subfolders_with_paths))print(f"Scandir: {timeit.timeit(a, number=1000):.3f}")print(f"Listdir: {timeit.timeit(b, number=1000):.3f}")print(f"Walk: {timeit.timeit(c, number=1000):.3f}")print(f"Glob: {timeit.timeit(d, number=1000):.3f}")print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")print(f"Pathlib: {timeit.timeit(f, number=1000):.3f}")
Dlaczego nikt o tym nie wspomniał glob? globpozwala korzystać z rozwijania nazw ścieżek w stylu uniksowym i jest moim działaniem dla prawie wszystkiego, co musi znaleźć więcej niż jedną nazwę ścieżki. To bardzo ułatwia:
from glob import glob
paths = glob('*/')
Zauważ, że globzwróci katalog z końcowym ukośnikiem (tak jak zrobiłby to Unix), podczas gdy większość pathrozwiązań bazujących pomija końcowy ukośnik.
Dobre rozwiązanie, proste i działa. Dla tych, którzy nie chcą tego ostatecznego cięcia, może tego użyć paths = [ p.replace('/', '') for p in glob('*/') ].
Evan Hu
5
Bezpieczniej może być po prostu wyciąć ostatni znak za pomocą [p[:-1] for p in paths], ponieważ ta metoda replace zastąpi również wszelkie ukośniki w nazwie pliku (nie są one powszechne).
ari
3
Jeszcze bezpieczniej, użyj strip („/”), aby usunąć końcowe ukośniki. W ten sposób gwarantujesz, że nie usuniesz żadnych postaci, które nie są ukośnikami do przodu
Eliezer Miron
8
Dzięki konstrukcji masz gwarancję końcowego ukośnika (więc nie jest to bezpieczniejsze), ale myślę, że jest bardziej czytelny. Na pewno chcesz użyć rstripzamiast strip, ponieważ ta ostatnia zamieni wszystkie w pełni kwalifikowane ścieżki w ścieżki względne.
ari
7
uzupełnienie komentarza @ari dla początkujących pythona, takich jak I: strip('/')usunie zarówno początkowy, jak i końcowy rstrip('/')
Niezwykle sprytny. Chociaż wydajność nie ma znaczenia ( ... całkowicie ma znaczenie ), jestem ciekawy, czy to lub wyrażenie generatora oparte na globach (s.rstrip("/") for s in glob(parent_dir+"*/"))jest bardziej wydajne czasowo. Moje intuicyjne podejrzenie jest takie, że rozwiązanie stat()oparte na a powinno być znacznie szybsze niż globbing w stylu powłoki. Niestety brakuje mi chęci i rzeczywiście się tego dowiem. os.walk()timeit
Cecil Curry
3
Zwróć uwagę, że zwraca to nazwy podkatalogów bez poprzedzonej nazwy katalogu nadrzędnego.
Paul Chernoch,
19
import os, os.path
Aby uzyskać (pełną ścieżkę) bezpośrednie podkatalogi w katalogu:
defSubDirPath(d):return filter(os.path.isdir,[os.path.join(d,f)for f in os.listdir(d)])
walk () generuje nazwy plików w drzewie katalogów, przechodząc po drzewie od góry do dołu lub od dołu do góry. Dla każdego katalogu w drzewie zakorzenionym na szczycie katalogu (w tym samego wierzchołka), zwraca 3-krotkę (dirpath, dirnames, filenames).
from twisted.python.filepath importFilePathdef subdirs(pathObj):for subpath in pathObj.walk():if subpath.isdir():yield subpath
if __name__ =='__main__':for subdir in subdirs(FilePath(".")):print"Subdirectory:", subdir
Ponieważ niektórzy komentatorzy pytali, jakie są zalety korzystania z bibliotek Twisted w tym celu, wyjdę nieco poza oryginalne pytanie.
Dokładniej w tym przykładzie: w przeciwieństwie do standardowej wersji biblioteki, tę funkcję można zaimplementować bez importu . Funkcja „subdirs” jest całkowicie ogólna, ponieważ działa tylko na swoim argumencie. Aby kopiować i przenosić pliki przy użyciu standardowej biblioteki, musisz polegać na " open" wbudowanym " listdir", być może " isdir" lub " os.walk" lub " shutil.copy". Może też " os.path.join". Nie wspominając już o tym, że potrzebujesz ciągu znaków przekazujących argument, aby zidentyfikować rzeczywisty plik. Przyjrzyjmy się pełnej implementacji, która skopiuje „index.tpl” każdego katalogu do „index.html”:
def copyTemplates(topdir):for subdir in subdirs(topdir):
tpl = subdir.child("index.tpl")if tpl.exists():
tpl.copyTo(subdir.child("index.html"))
Powyższa funkcja „subdirs” może działać na każdym FilePathpodobnym obiekcie. Co oznacza między innymi ZipPathprzedmioty. Niestety ZipPathjest teraz tylko do odczytu, ale można go rozszerzyć, aby obsługiwał pisanie.
Możesz także przekazać własne obiekty do celów testowych. Aby przetestować sugerowane tutaj interfejsy API wykorzystujące os.path, musisz małpować zaimportowane nazwy i niejawne zależności i generalnie wykonać czarną magię, aby testy działały. Z FilePath możesz zrobić coś takiego:
classMyFakePath:def child(self, name):"Return an appropriate child object"def walk(self):"Return an iterable of MyFakePath objects"def exists(self):"Return true or false, as appropriate to the test"def isdir(self):"Return true or false, as appropriate to the test"...
subdirs(MyFakePath(...))
Ponieważ mam niewielki kontakt z Twisted, zawsze z zadowoleniem przyjmuję dodatkowe informacje i przykłady; ta odpowiedź jest miła do zobaczenia. Powiedziawszy to, skoro takie podejście wydaje się wymagać znacznie więcej pracy niż użycie wbudowanych modułów Pythona i instalacji Twisted, czy są jakieś zalety korzystania z tego, które możesz dodać do odpowiedzi?
Jarret Hardie
1
Odpowiedź Glypha była prawdopodobnie zainspirowana faktem, że TwistedLore używa również plików .tpl.
Constantin
Cóż, najwyraźniej nie spodziewam się hiszpańskiej inkwizycji :-) Założyłem, że „* .tpl” jest ogólnym odniesieniem do jakiegoś abstrakcyjnego rozszerzenia oznaczającego „szablon”, a nie konkretnego szablonu Twisted (widziałem .tpl używany w wielu języki w końcu). Dobrze wiedzieć.
Jarret Hardie
W związku z tym +1 za obrócenie do możliwego kąta Twisted, chociaż nadal chciałbym zrozumieć, co obiekt Twisted'd 'FilePath' i funkcja 'walk ()' dodają do standardowego interfejsu API.
Jarret Hardie
Osobiście uważam, że „FilePath.walk () daje obiekty ścieżek” o wiele łatwiejsze do zapamiętania niż „os.walk daje 3-krotki dir, dirs, files”. Ale są też inne korzyści. FilePath dopuszcza polimorfizm, co oznacza, że możesz przechodzić przez rzeczy inne niż systemy plików. Na przykład, możesz przekazać twisted.python.zippath.ZipArchive do mojej funkcji „subdirs” i pobrać generator ścieżek ZipPath zamiast FilePaths; Twoja logika się nie zmienia, ale Twoja aplikacja w magiczny sposób obsługuje pliki zip. Jeśli chcesz to przetestować, wystarczy dostarczyć obiekt, nie musisz pisać prawdziwych plików.
Glyph
4
Właśnie napisałem kod do przenoszenia maszyn wirtualnych vmware i ostatecznie użyłem os.pathi shutilwykonałem kopiowanie plików między podkatalogami.
-1: nie zadziała, ponieważ shutil.copy skopiuje do bieżącego katalogu, więc skończysz nadpisując „index.html” w bieżącym katalogu raz dla każdego „index.tpl”, który znajdziesz w drzewie podkatalogów.
nosklo
1
Muszę wspomnieć o bibliotece path.py , z której bardzo często korzystam.
Pobieranie bezpośrednich podkatalogów staje się tak proste:
my_dir.dirs()
Pełny przykład roboczy to:
from path importPath
my_directory =Path("path/to/my/directory")
subdirs = my_directory.dirs()
NB: my_directory nadal można manipulować jako ciąg znaków, ponieważ Path jest podklasą łańcucha, ale zapewnia kilka użytecznych metod manipulowania ścieżkami
robię fajnie, wersja pythona 3.6, ale musiałem skasować "self", od wewnątrz zmienne funkcyjne
locometro
1
używał wewnątrz klasy, zaktualizował
Kanish Mathew
0
import glob
import os
def child_dirs(path):
cd = os.getcwd()# save the current working directory
os.chdir(path)# change directory
dirs = glob.glob("*/")# get all the subdirectories
os.chdir(cd)# change directory to the script original locationreturn dirs
child_dirsFunkcja przyjmuje ścieżkę katalogu i zwraca listę bezpośrednich podkatalogów w nim.
dir
|-- dir_1
-- dir_2
child_dirs('dir')->['dir_1','dir_2']
Odpowiedzi:
Przeprowadziłem testy szybkości różnych funkcji, aby zwrócić pełną ścieżkę do wszystkich bieżących podkatalogów.
tl; dr: Zawsze używaj
scandir
:list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
Bonus: dzięki
scandir
możesz także po prostu uzyskać nazwy folderów tylko za pomocąf.name
zamiastf.path
.Ta (podobnie jak wszystkie inne funkcje poniżej) nie będzie używać naturalnego sortowania . Oznacza to, że wyniki będą sortowane w następujący sposób: 1, 10, 2. Aby uzyskać naturalne sortowanie (1, 2, 10), spójrz na https://stackoverflow.com/a/48030307/2441026
Wyniki :
scandir
to: 3x szybciej niżwalk
, 32x szybciej niżlistdir
(z filtrem), 35x szybciej niżPathlib
i 36x szybciej niżlistdir
i 37x (!) Szybciej niżglob
.Testowane z W7x64, Python 3.8.1. Folder z 440 podfolderami.
Jeśli zastanawiasz się, czy
listdir
można by przyspieszyć, nie wykonując dwukrotnie os.path.join (), tak, ale różnica w zasadzie nie istnieje.Kod:
źródło
źródło
Dlaczego nikt o tym nie wspomniał
glob
?glob
pozwala korzystać z rozwijania nazw ścieżek w stylu uniksowym i jest moim działaniem dla prawie wszystkiego, co musi znaleźć więcej niż jedną nazwę ścieżki. To bardzo ułatwia:Zauważ, że
glob
zwróci katalog z końcowym ukośnikiem (tak jak zrobiłby to Unix), podczas gdy większośćpath
rozwiązań bazujących pomija końcowy ukośnik.źródło
paths = [ p.replace('/', '') for p in glob('*/') ]
.[p[:-1] for p in paths]
, ponieważ ta metoda replace zastąpi również wszelkie ukośniki w nazwie pliku (nie są one powszechne).rstrip
zamiaststrip
, ponieważ ta ostatnia zamieni wszystkie w pełni kwalifikowane ścieżki w ścieżki względne.strip('/')
usunie zarówno początkowy, jak i końcowyrstrip('/')
Zaznacz " Pobieranie listy wszystkich podkatalogów w bieżącym katalogu ".
Oto wersja Pythona 3:
źródło
(s.rstrip("/") for s in glob(parent_dir+"*/"))
jest bardziej wydajne czasowo. Moje intuicyjne podejrzenie jest takie, że rozwiązaniestat()
oparte na a powinno być znacznie szybsze niż globbing w stylu powłoki. Niestety brakuje mi chęci i rzeczywiście się tego dowiem.os.walk()
timeit
Aby uzyskać (pełną ścieżkę) bezpośrednie podkatalogi w katalogu:
Aby uzyskać najnowszy (najnowszy) podkatalog:
źródło
list( filter(...) )
.os.walk
jest twoim przyjacielem w tej sytuacji.Prosto z dokumentacji:
źródło
Ta metoda ładnie robi wszystko za jednym razem.
źródło
Korzystanie z modułu FilePath Twisted:
Ponieważ niektórzy komentatorzy pytali, jakie są zalety korzystania z bibliotek Twisted w tym celu, wyjdę nieco poza oryginalne pytanie.
W gałęzi jest ulepszona dokumentacja, która wyjaśnia zalety FilePath; możesz to przeczytać.
Dokładniej w tym przykładzie: w przeciwieństwie do standardowej wersji biblioteki, tę funkcję można zaimplementować bez importu . Funkcja „subdirs” jest całkowicie ogólna, ponieważ działa tylko na swoim argumencie. Aby kopiować i przenosić pliki przy użyciu standardowej biblioteki, musisz polegać na "
open
" wbudowanym "listdir
", być może "isdir
" lub "os.walk
" lub "shutil.copy
". Może też "os.path.join
". Nie wspominając już o tym, że potrzebujesz ciągu znaków przekazujących argument, aby zidentyfikować rzeczywisty plik. Przyjrzyjmy się pełnej implementacji, która skopiuje „index.tpl” każdego katalogu do „index.html”:Powyższa funkcja „subdirs” może działać na każdym
FilePath
podobnym obiekcie. Co oznacza między innymiZipPath
przedmioty. NiestetyZipPath
jest teraz tylko do odczytu, ale można go rozszerzyć, aby obsługiwał pisanie.Możesz także przekazać własne obiekty do celów testowych. Aby przetestować sugerowane tutaj interfejsy API wykorzystujące os.path, musisz małpować zaimportowane nazwy i niejawne zależności i generalnie wykonać czarną magię, aby testy działały. Z FilePath możesz zrobić coś takiego:
źródło
Właśnie napisałem kod do przenoszenia maszyn wirtualnych vmware i ostatecznie użyłem
os.path
ishutil
wykonałem kopiowanie plików między podkatalogami.Nie jest zbyt elegancka, ale działa.
źródło
Oto jeden sposób:
źródło
Muszę wspomnieć o bibliotece path.py , z której bardzo często korzystam.
Pobieranie bezpośrednich podkatalogów staje się tak proste:
my_dir.dirs()
Pełny przykład roboczy to:
źródło
Następującą funkcję można wywołać jako:
get_folders_in_directories_recursively (directory, index = 1) -> podaje listę folderów na pierwszym poziomie
get_folders_in_directories_recursively (katalog) -> podaje wszystkie podfoldery
źródło
child_dirs
Funkcja przyjmuje ścieżkę katalogu i zwraca listę bezpośrednich podkatalogów w nim.źródło
źródło
Jedna linijka używająca pathlib:
źródło