So erhalten Sie alle unmittelbaren Unterverzeichnisse in Python


150

Ich versuche, ein einfaches Python-Skript zu schreiben, das eine index.tpl in index.html in allen Unterverzeichnissen kopiert (mit wenigen Ausnahmen).

Ich bin festgefahren, wenn ich versuche, die Liste der Unterverzeichnisse abzurufen.


11
Sie können feststellen, dass die akzeptierte Antwort auf diese frühere SO-Frage das Problem löst: stackoverflow.com/questions/120656/directory-listing-in-python
Jarret Hardie

Antworten:


31

Ich habe einige Geschwindigkeitstests für verschiedene Funktionen durchgeführt, um den vollständigen Pfad zu allen aktuellen Unterverzeichnissen wiederherzustellen.

tl; dr: Verwenden Sie immer scandir:

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

Bonus: Mit können scandirSie auch einfach nur Ordnernamen abrufen, indem Sie f.namestatt verwenden f.path.

Dies (wie auch alle anderen Funktionen unten) verwendet keine natürliche Sortierung . Dies bedeutet, dass die Ergebnisse wie folgt sortiert werden: 1, 10, 2. Um eine natürliche Sortierung (1, 2, 10) zu erhalten, besuchen Sie bitte https://stackoverflow.com/a/48030307/2441026




Ergebnisse : scandirist: 3x schneller als walk, 32x schneller als listdir(mit Filter), 35x schneller als Pathlibund 36x schneller als listdirund 37x (!) Schneller als glob.

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

Getestet mit W7x64, Python 3.8.1. Ordner mit 440 Unterordnern.
Falls Sie sich fragen, ob Sie listdires beschleunigen könnten , wenn Sie os.path.join () nicht zweimal ausführen, ja, aber der Unterschied ist im Grunde nicht vorhanden.

Code:

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
Ich möchte mich nur bei Ihnen bedanken, habe wirklich danach gesucht. Tolle Analyse.
Cing

224
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

Warum hat niemand erwähnt glob? globErmöglicht die Verwendung der Pfadnamenerweiterung im Unix-Stil und ist meine Funktion für fast alles, was benötigt wird, um mehr als einen Pfadnamen zu finden. Das macht es sehr einfach:

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

Beachten Sie, dass globdas Verzeichnis mit dem endgültigen Schrägstrich zurückgegeben wird (wie es Unix tun würde), während die meisten pathbasierten Lösungen den endgültigen Schrägstrich weglassen.


3
Gute Lösung, einfach und funktioniert. Für diejenigen, die diesen letzten Schrägstrich nicht wollen, kann er diesen verwenden paths = [ p.replace('/', '') for p in glob('*/') ].
Evan Hu

5
Es ist möglicherweise sicherer, einfach das letzte Zeichen mit zu schneiden [p[:-1] for p in paths], da diese Ersetzungsmethode auch alle maskierten Schrägstriche im Dateinamen ersetzt (nicht, dass diese häufig vorkommen).
Ari

3
Verwenden Sie noch sicherer einen Streifen ('/'), um nachgestellte Schrägstriche zu entfernen. Dieser Weg garantiert, dass Sie keine Zeichen ausschneiden, die keine Schrägstriche sind
Eliezer Miron

8
Durch die Konstruktion ist garantiert, dass Sie einen abschließenden Schrägstrich haben (es ist also nicht sicherer), aber ich denke, dass er besser lesbar ist. Sie möchten jedoch definitiv rstripanstelle von verwenden strip, da letztere alle vollständig qualifizierten Pfade in relative Pfade umwandeln.
Ari

7
Ergänzung zum @ari-Kommentar für Python-Neulinge wie I: Entfernt strip('/')sowohl Start- als auch Nachlauf '/', rstrip('/')entfernt nur den Nachlauf
Titou

35

Aktivieren Sie " Liste aller Unterverzeichnisse im aktuellen Verzeichnis abrufen ".

Hier ist eine Python 3-Version:

import os

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

print(dir_list)

2
Extrem klug. Während Effizienz keine Rolle spielt ( ... es ist völlig wichtig ), bin ich gespannt, ob dieser oder der globbasierte Generatorausdruck zeiteffizienter (s.rstrip("/") for s in glob(parent_dir+"*/"))ist. Mein intuitiver Verdacht besteht , dass eine stat()-basierte os.walk()Lösung sollte tiefgreifend schneller als Shell-Stil Globbing. Leider fehlt mir der Wille, es timeittatsächlich herauszufinden.
Cecil Curry

3
Beachten Sie, dass dadurch die Namen des Unterverzeichnisses zurückgegeben werden, ohne dass der Name des übergeordneten Verzeichnisses vorangestellt ist.
Paul Chernoch

19
import os, os.path

So rufen Sie (vollständige) unmittelbare Unterverzeichnisse in einem Verzeichnis ab:

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

So erhalten Sie das neueste (neueste) Unterverzeichnis:

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

Um eine Liste zu erhalten , fügen Sie einfach hinzu list( filter(...) ).
user136036

12

os.walk ist dein Freund in dieser Situation.

Direkt aus der Dokumentation:

walk () generiert die Dateinamen in einem Verzeichnisbaum, indem der Baum entweder von oben nach unten oder von unten nach oben verschoben wird. Für jedes Verzeichnis in der Baumstruktur, das oben im Verzeichnis verwurzelt ist (einschließlich top selbst), wird ein 3-Tupel (dirpath, dirnames, filenames) ausgegeben.


1
Beachten Sie jedoch, dass Sie, wenn Sie nur die Unterverzeichnisse der ersten Ebene möchten, nach dem ersten Satz von Rückgabewerten aus der os.walk-Iteration ausbrechen.
Yooy

11

Diese Methode macht alles auf einmal.

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

7

Verwenden des FilePath-Moduls von 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

Da einige Kommentatoren gefragt haben, welche Vorteile die Verwendung von Twisted-Bibliotheken dafür hat, gehe ich hier etwas über die ursprüngliche Frage hinaus.


In einem Zweig gibt es eine verbesserte Dokumentation, in der die Vorteile von FilePath erläutert werden. Vielleicht möchten Sie das lesen.

Genauer gesagt in diesem Beispiel: Im Gegensatz zur Standardbibliotheksversion kann diese Funktion ohne Importe implementiert werden . Die "subdirs" -Funktion ist völlig generisch, da sie nur mit ihrem Argument arbeitet. Um die Dateien mit der Standardbibliothek zu kopieren und zu verschieben, müssen Sie sich auf " open", listdir", vielleicht" isdir"oder" os.walk"oder" shutil.copy"verlassen. Vielleicht "os.path.join auch. Ganz zu schweigen von der Tatsache, dass Sie eine Zeichenfolge benötigen, die ein Argument übergeben hat, um die tatsächliche Datei zu identifizieren. Werfen wir einen Blick auf die vollständige Implementierung, mit der die "index.tpl" jedes Verzeichnisses nach "index.html" kopiert wird:

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

Die obige Funktion "Unterverzeichnisse" kann für jedes FilePathähnliche Objekt verwendet werden. Was unter anderem bedeutet,ZipPath Objekte bedeutet. Leider ZipPathist es momentan schreibgeschützt, aber es könnte erweitert werden, um das Schreiben zu unterstützen.

Sie können auch Ihre eigenen Objekte zu Testzwecken übergeben. Um die hier vorgeschlagenen APIs zu testen, die os.path verwenden, müssen Sie Affen mit importierten Namen und impliziten Abhängigkeiten verwenden und im Allgemeinen schwarze Magie ausführen, damit Ihre Tests funktionieren. Mit FilePath machen Sie ungefähr Folgendes:

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(...))

Da ich wenig mit Twisted zu tun habe, freue ich mich immer über zusätzliche Informationen und Beispiele. Diese Antwort ist schön zu sehen. Gibt es jedoch Vorteile, die Sie der Antwort hinzufügen könnten, da dieser Ansatz wesentlich mehr Arbeit erfordert als die Verwendung der integrierten Python-Module und eine Twisted-Installation?
Jarret Hardie

1
Glyphs Antwort wurde wahrscheinlich von der Tatsache inspiriert, dass TwistedLore auch .tpl-Dateien verwendet.
Constantin

Natürlich erwarte ich die spanische Inquisition nicht :-) Ich nahm an, dass "* .tpl" ein allgemeiner Verweis auf eine abstrakte Erweiterung ist, die "Vorlage" bedeutet, und nicht auf eine bestimmte verdrehte Vorlage (ich habe gesehen, dass .tpl in vielen verwendet wird Sprachen). Gut zu wissen.
Jarret Hardie

+1 daher für das Twigging auf den möglichen Twisted-Winkel, obwohl ich immer noch gerne verstehen möchte, was das Twisted'd-Objekt 'FilePath' und die Funktion 'walk ()' zur Standard-API hinzufügen.
Jarret Hardie

Persönlich finde ich, dass "FilePath.walk () Pfadobjekte liefert" viel einfacher zu merken ist als "os.walk liefert 3-Tupel von dir, dirs, files". Es gibt aber noch andere Vorteile. FilePath ermöglicht Polymorphismus, dh Sie können andere Dinge als Dateisysteme durchlaufen. Sie könnten beispielsweise ein twisted.python.zippath.ZipArchive an meine 'subdirs'-Funktion übergeben und einen Generator von ZipPaths anstelle von FilePaths ausgeben. Ihre Logik ändert sich nicht, aber Ihre Anwendung verarbeitet Zip-Dateien jetzt auf magische Weise. Wenn Sie es testen möchten, müssen Sie nur ein Objekt angeben, Sie müssen keine echten Dateien schreiben.
Glyphe

4

Ich habe gerade Code geschrieben, um virtuelle VMware-Maschinen zu verschieben, und schließlich das Kopieren von Dateien zwischen Unterverzeichnissen verwendet os.pathund shutildurchgeführt.

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

Es ist nicht besonders elegant, aber es funktioniert.


1

Hier ist eine Möglichkeit:

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: funktioniert nicht, da shutil.copy in das aktuelle Verzeichnis kopiert wird, sodass Sie "index.html" im aktuellen Verzeichnis einmal für jede "index.tpl" überschreiben, die Sie im Unterverzeichnisbaum finden.
Nosklo

1

Ich muss die Bibliothek path.py erwähnen , die ich sehr oft benutze.

Das Abrufen der unmittelbaren Unterverzeichnisse wird so einfach:

my_dir.dirs()

Das vollständige Arbeitsbeispiel lautet:

from path import Path

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

subdirs = my_directory.dirs()

NB: my_directory kann weiterhin als Zeichenfolge bearbeitet werden, da Path eine Unterklasse von Zeichenfolgen ist, jedoch eine Reihe nützlicher Methoden zum Bearbeiten von Pfaden bereitstellt


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

Die folgende Funktion kann aufgerufen werden als:

get_folders_in_directories_recursively (Verzeichnis, Index = 1) -> gibt die Liste der Ordner in der ersten Ebene an

get_folders_in_directories_recursively (Verzeichnis) -> gibt alle Unterordner an


Ich mache es gut, Version Python 3.6, aber ich musste "self" aus den inneren Funktionsvariablen löschen
locometro

1
wurde in einer Klasse verwendet, habe aktualisiert
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 location
     return dirs

Die child_dirsFunktion nimmt einen Pfad in ein Verzeichnis und gibt eine Liste der unmittelbaren Unterverzeichnisse darin zurück.

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

Ein Liner mit Pathlib:

list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.