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.
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.
Antworten:
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 scandir
Sie auch einfach nur Ordnernamen abrufen, indem Sie f.name
statt 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 :
scandir
ist: 3x schneller als walk
, 32x schneller als listdir
(mit Filter), 35x schneller als Pathlib
und 36x schneller als listdir
und 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 listdir
es 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}")
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))]
Warum hat niemand erwähnt glob
? glob
Ermö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 glob
das Verzeichnis mit dem endgültigen Schrägstrich zurückgegeben wird (wie es Unix tun würde), während die meisten path
basierten Lösungen den endgültigen Schrägstrich weglassen.
paths = [ p.replace('/', '') for p in glob('*/') ]
.
[p[:-1] for p in paths]
, da diese Ersetzungsmethode auch alle maskierten Schrägstriche im Dateinamen ersetzt (nicht, dass diese häufig vorkommen).
rstrip
anstelle von verwenden strip
, da letztere alle vollständig qualifizierten Pfade in relative Pfade umwandeln.
strip('/')
sowohl Start- als auch Nachlauf '/', rstrip('/')
entfernt nur den Nachlauf
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)
(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 timeit
tatsächlich herauszufinden.
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)
list( filter(...) )
.
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.
Diese Methode macht alles auf einmal.
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
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 ZipPath
ist 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(...))
Ich habe gerade Code geschrieben, um virtuelle VMware-Maschinen zu verschieben, und schließlich das Kopieren von Dateien zwischen Unterverzeichnissen verwendet os.path
und shutil
durchgefü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.
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')
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
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
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_dirs
Funktion 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']
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')
Ein Liner mit Pathlib:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]