Come ottenere tutte le sottodirectory immediate in Python


Risposte:


31

Ho fatto alcuni test di velocità su varie funzioni per restituire il percorso completo a tutte le sottodirectory correnti.

tl; dr: usa sempre scandir:

list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]

Bonus: Con scandirte puoi anche semplicemente ottenere i nomi delle cartelle usando f.nameinvece di f.path.

Questo (così come tutte le altre funzioni seguenti) non utilizzerà l'ordinamento naturale . Ciò significa che i risultati verranno ordinati in questo modo: 1, 10, 2. Per ottenere l'ordinamento naturale (1, 2, 10), dai un'occhiata a https://stackoverflow.com/a/48030307/2441026




Risultati : scandirè 3 volte più veloce di walk, 32 volte più veloce di listdir(con filtro), 35 volte più veloce di Pathlibe 36 volte più veloce di listdire 37x (!) Più veloce di glob.

Scandir:           0.977
Walk:              3.011
Listdir (filter): 31.288
Pathlib:          34.075
Listdir:          35.501
Glob:             36.277

Testato con W7x64, Python 3.8.1. Cartella con 440 sottocartelle.
Nel caso ti chiedi se listdirpotresti accelerare non facendo os.path.join () due volte, sì, ma la differenza è sostanzialmente inesistente.

Codice:

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}")

1
Voglio solo ringraziarti, stavo davvero cercando questo. Ottima analisi.
Cing

225
import os
def get_immediate_subdirectories(a_dir):
    return [name for name in os.listdir(a_dir)
            if os.path.isdir(os.path.join(a_dir, name))]

76

Perché nessuno ha menzionato glob? globti permette di usare l'espansione del percorso in stile Unix, ed è il mio go per funzionare per quasi tutto ciò che ha bisogno di trovare più di un nome di percorso. Lo rende molto semplice:

from glob import glob
paths = glob('*/')

Si noti che globrestituirà la directory con la barra finale (come farebbe unix) mentre la maggior parte delle pathsoluzioni basate ometterà la barra finale.


3
Buona soluzione, semplice e funzionante. Per coloro che non vogliono quella barra finale, può usare questo paths = [ p.replace('/', '') for p in glob('*/') ].
Evan Hu,

5
Potrebbe essere più sicuro tagliare semplicemente l'ultimo carattere con [p[:-1] for p in paths], poiché quel metodo di sostituzione sostituirà anche qualsiasi barra in avanti evasa nel nome del file (non che quelli siano comuni).
ari,

3
Ancora più sicuro, usa la striscia ('/') per rimuovere le barre finali. In questo modo ti garantiamo di non ritagliare alcun personaggio che non sia barra rovesciata
Eliezer Miron

8
Per costruzione, hai la garanzia di avere una barra (quindi non è più sicura), ma penso che sia più leggibile. Tuttavia, si desidera utilizzare rstripinvece invece strip, poiché quest'ultimo trasformerà tutti i percorsi completi in percorsi relativi.
ari

7
complemento al commento @ari per i neofiti di Python come I: strip('/')rimuoverà "/" iniziale e finale,rstrip('/') rimuoverà solo quello finale
Titou

35

Seleziona " Ottenere un elenco di tutte le sottodirectory nella directory corrente ".

Ecco una versione di Python 3:

import os

dir_list = next(os.walk('.'))[1]

print(dir_list)

2
Estremamente intelligente. Mentre l'efficienza non ha importanza ( ... lo è totalmente ), sono curioso di sapere se questa o l'espressione del generatore basata su glob (s.rstrip("/") for s in glob(parent_dir+"*/"))è più efficiente in termini di tempo. Il mio intuitivo sospetto è che una soluzione stat()basata su basi dovrebbe essere profondamente più veloce dei globbing in stile shell. Purtroppo, mi manca la volontà di e davvero scoprire. os.walk()timeit
Cecil Curry,

3
Si noti che ciò restituisce i nomi della sottodirectory senza il nome della directory padre con prefisso.
Paul Chernoch,

19
import os, os.path

Per ottenere sottodirectory immediate (percorso completo) in una directory:

def SubDirPath (d):
    return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])

Per ottenere la sottodirectory più recente (più recente):

def LatestDirectory (d):
    return max(SubDirPath(d), key=os.path.getmtime)

Per ottenere un elenco , è sufficiente aggiungere list( filter(...) ).
user136036

12

os.walk è tuo amico in questa situazione.

Direttamente dalla documentazione:

walk () genera i nomi dei file in un albero di directory, camminando l'albero in alto o in basso. Per ogni directory nell'albero radicata nella directory top (incluso top stesso), produce una 3 tupla (dirpath, dirnames, nomi di file).


1
Basta essere consapevoli del fatto che se si desidera solo le sottodirectory di primo livello, uscire dall'iterazione os.walk dopo il primo set di valori restituiti.
yoyo

11

Questo metodo fa bene tutto in una volta.

from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]

7

Utilizzando il modulo FilePath di Twisted:

from twisted.python.filepath import FilePath

def subdirs(pathObj):
    for subpath in pathObj.walk():
        if subpath.isdir():
            yield subpath

if __name__ == '__main__':
    for subdir in subdirs(FilePath(".")):
        print "Subdirectory:", subdir

Poiché alcuni commentatori hanno chiesto quali siano i vantaggi dell'utilizzo delle librerie di Twisted per questo, andrò un po 'oltre la domanda originale qui.


C'è una documentazione migliorata in un ramo che spiega i vantaggi di FilePath; potresti volerlo leggere.

Più specificamente in questo esempio: a differenza della versione standard della libreria, questa funzione può essere implementata senza importazioni . La funzione "subdir" è totalmente generica, in quanto opera solo sul suo argomento. Per copiare e spostare i file utilizzando la libreria standard, è necessario dipendere da " open" incorporato "," listdir", forse" isdir"o" os.walk"o" shutil.copy". Forse " os.path.join" anche. Per non parlare del fatto che è necessario che una stringa abbia passato un argomento per identificare il file effettivo. Diamo un'occhiata all'implementazione completa che copierà "index.tpl" di ciascuna directory in "index.html":

def copyTemplates(topdir):
    for subdir in subdirs(topdir):
        tpl = subdir.child("index.tpl")
        if tpl.exists():
            tpl.copyTo(subdir.child("index.html"))

La funzione "subdir" sopra può funzionare su qualsiasi FilePathoggetto simile. Il che significa, tra le altre cose, ZipPathoggetti. Purtroppo al momento ZipPathè di sola lettura, ma potrebbe essere esteso per supportare la scrittura.

Puoi anche passare i tuoi oggetti a scopo di test. Per testare le API che utilizzano os.path suggerite qui, devi cercare i nomi importati e le dipendenze implicite ed eseguire generalmente la magia nera per far funzionare i test. Con FilePath, fai qualcosa del genere:

class MyFakePath:
    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(...))

Dato che ho poca esposizione a Twisted, accolgo sempre con favore ulteriori informazioni ed esempi; questa risposta è piacevole da vedere. Detto questo, dal momento che questo approccio sembra richiedere molto più lavoro rispetto all'utilizzo dei moduli python integrati e un'installazione Twisted, ci sono dei vantaggi nell'usarlo che potresti aggiungere alla risposta?
Jarret Hardie,

1
La risposta di Glyph è stata probabilmente ispirata dal fatto che TwistedLore utilizza anche file .tpl.
Constantin,

Bene, chiaramente non mi aspetto l'inquisizione spagnola :-) Ho supposto che "* .tpl" fosse un riferimento generico ad un'estensione astratta che significa "modello", e non un modello Twisted specifico (ho visto .tpl usato in molti dopo tutto le lingue). Buono a sapersi.
Jarret Hardie,

+1 quindi per passare al possibile angolo contorto, anche se mi piacerebbe ancora capire quali oggetti Twisted 'FilePath' e la funzione 'walk ()' aggiungono all'API standard.
Jarret Hardie,

Personalmente trovo che "FilePath.walk () restituisce oggetti path" molto più facile da ricordare di "os.walk produce 3 tuple di dir, dirs, file". Ma ci sono altri vantaggi. FilePath consente il polimorfismo, il che significa che puoi attraversare cose diverse dai filesystem. Ad esempio, potresti passare un twisted.python.zippath.ZipArchive alla mia funzione 'subdir' e far uscire un generatore di ZipPath invece di FilePaths; la tua logica non cambia, ma l'applicazione ora gestisce magicamente i file zip. Se vuoi provarlo, devi solo fornire un oggetto, non devi scrivere file reali.
Glifo

4

Ho appena scritto un po 'di codice per spostare le macchine virtuali VMware e ho finito per usare os.pathe shutilrealizzare la copia dei file tra le sottodirectory.

def copy_client_files (file_src, file_dst):
    for file in os.listdir(file_src):
            print "Copying file: %s" % file
            shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))

Non è terribilmente elegante, ma funziona.


1

Ecco un modo:

import os
import shutil

def copy_over(path, from_name, to_name):
  for path, dirname, fnames in os.walk(path):
    for fname in fnames:
      if fname == from_name:
        shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))


copy_over('.', 'index.tpl', 'index.html')

-1: non funzionerà, poiché shutil.copy verrà copiato nella directory corrente, quindi finirai per sovrascrivere 'index.html' nella directory corrente una volta per ogni 'index.tpl' che trovi nella struttura della sottodirectory.
nosklo,

1

Devo menzionare il path.py libreria , che uso molto spesso.

Recuperare le sottodirectory immediate diventa così semplice:

my_dir.dirs()

L'esempio di lavoro completo è:

from path import Path

my_directory = Path("path/to/my/directory")

subdirs = my_directory.dirs()

NB: my_directory può ancora essere manipolata come una stringa, poiché Path è una sottoclasse di string, ma fornisce un sacco di metodi utili per manipolare i percorsi


1
def get_folders_in_directories_recursively(directory, index=0):
    folder_list = list()
    parent_directory = directory

    for path, subdirs, _ in os.walk(directory):
        if not index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)
        elif path[len(parent_directory):].count('/') + 1 == index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)

    return folder_list

La seguente funzione può essere chiamata come:

get_folders_in_directories_recursively (directory, index = 1) -> fornisce l'elenco delle cartelle al primo livello

get_folders_in_directories_recursively (directory) -> fornisce tutte le sottocartelle


facendo bene, versione python 3.6, ma avevo bisogno di cancellare "self", dalle variabili di funzione interne
locometro

1
stava usando all'interno di una classe, ho aggiornato
Kanish Mathew il

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 location
     return dirs

La child_dirsfunzione prende un percorso in una directory e restituisce un elenco delle sottodirectory immediate in essa contenute.

dir
 |
  -- dir_1
  -- dir_2

child_dirs('dir') -> ['dir_1', 'dir_2']

0
import pathlib


def list_dir(dir):
    path = pathlib.Path(dir)
    dir = []
    try:
        for item in path.iterdir():
            if item.is_dir():
                dir.append(item)
        return dir
    except FileNotFoundError:
        print('Invalid directory')

0

Un liner usando pathlib:

list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.