Frage Wie kann ich ein verschachteltes Verzeichnis in Python erstellen?


Was ist der eleganteste Weg zu prüfen, ob das Verzeichnis, in das eine Datei geschrieben wird, existiert, und wenn nicht, das Verzeichnis mit Python erstellen? Hier ist was ich versucht habe:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

Irgendwie habe ich es vermisst os.path.exists (Danke Kanja, Blair und Douglas). Das habe ich jetzt:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

Gibt es eine Flagge für "offen", das macht das automatisch?


2983
2017-11-07 18:56


Ursprung


Antworten:


Ich sehe zwei Antworten mit guten Eigenschaften, jede mit einem kleinen Fehler, also werde ich darauf eingehen:

Versuchen os.path.existsund bedenke os.makedirs für die Schaffung.

import os
if not os.path.exists(directory):
    os.makedirs(directory)

Wie in Kommentaren und anderswo angemerkt, gibt es eine Race-Bedingung - wenn das Verzeichnis zwischen den erstellt wird os.path.exists und das os.makedirs Anrufe, die os.makedirs wird mit einem scheitern OSError. Leider, penetrant OSError und Fortfahren ist nicht idiotensicher, da ein Fehler beim Erstellen des Verzeichnisses aufgrund anderer Faktoren wie unzureichende Berechtigungen, vollständige Festplatte usw. ignoriert wird.

Eine Möglichkeit wäre, die OSError und untersuchen Sie den eingebetteten Fehlercode (siehe Gibt es einen plattformübergreifenden Weg Informationen von Pythons OSError zu erhalten?):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

Alternativ könnte es eine zweite geben os.path.exists, aber angenommen, ein anderer erstellt das Verzeichnis nach der ersten Prüfung, dann entfernt es vor der zweiten - wir könnten immer noch täuschen.

Abhängig von der Anwendung kann die Gefahr von gleichzeitigen Operationen mehr oder weniger als die Gefahr durch andere Faktoren wie Dateiberechtigungen sein. Der Entwickler müsste vor der Auswahl einer Implementierung mehr über die jeweilige zu entwickelnde Anwendung und ihre erwartete Umgebung wissen.


3679
2017-11-07 19:06



Python 3.5+:

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir wie oben verwendet, erstellt rekursiv das Verzeichnis und löst keine Ausnahme aus, wenn das Verzeichnis bereits existiert. Wenn Sie nicht möchten, dass die Eltern erstellt werden, überspringen Sie die parents Streit.

Python 3.2+:

Verwenden pathlib:

Wenn Sie können, installieren Sie die aktuelle pathlib Backport benannt pathlib2. Installieren Sie nicht den älteren nicht unterstützten Backport namens pathlib. Beziehen Sie sich als Nächstes auf den obigen Abschnitt Python 3.5+ und verwenden Sie dasselbe.

Wenn Sie Python 3.4 verwenden, obwohl es mit pathlibEs fehlt das Nützliche exist_ok Möglichkeit. Der Backport soll eine neuere und überlegene Implementierung von mkdir was diese fehlende Option beinhaltet.

Verwenden os:

import os
os.makedirs(path, exist_ok=True)

os.makedirs wie oben verwendet, erstellt rekursiv das Verzeichnis und löst keine Ausnahme aus, wenn das Verzeichnis bereits existiert. Es hat das optionale exist_ok Argument nur bei Verwendung von Python 3.2+ mit einem Standardwert von False. Dieses Argument existiert in Python 2.x bis 2.7 nicht. Daher ist eine manuelle Ausnahmebehandlung wie bei Python 2.7 nicht erforderlich.

Python 2.7+:

Verwenden pathlib:

Wenn Sie können, installieren Sie die aktuelle pathlib Backport benannt pathlib2. Installieren Sie nicht den älteren nicht unterstützten Backport namens pathlib. Beziehen Sie sich als Nächstes auf den obigen Abschnitt Python 3.5+ und verwenden Sie dasselbe.

Verwenden os:

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

Während eine naive Lösung zuerst verwenden kann os.path.isdir gefolgt von os.makedirs, die obige Lösung kehrt die Reihenfolge der beiden Operationen um. Auf diese Weise verhindert es eine allgemeine Race-Bedingung, die mit einem doppelten Versuch des Erstellens des Verzeichnisses zu tun hat, und disambiguiert auch Dateien von Verzeichnissen.

Beachten Sie, dass die Ausnahme erfasst und verwendet wird errno ist von begrenzter Nützlichkeit, weil OSError: [Errno 17] File exists, d.h. errno.EEXIST, wird sowohl für Dateien als auch für Verzeichnisse ausgelöst. Es ist zuverlässiger, einfach zu überprüfen, ob das Verzeichnis existiert.

Alternative:

mkpath erstellt das verschachtelte Verzeichnis und führt nichts aus, wenn das Verzeichnis bereits existiert. Dies funktioniert in Python 2 und 3.

import distutils.dir_util
distutils.dir_util.mkpath(path)

Pro Fehler 10948Eine schwerwiegende Einschränkung dieser Alternative besteht darin, dass sie nur einmal pro Python-Prozess für einen bestimmten Pfad arbeitet. Mit anderen Worten, wenn Sie es verwenden, um ein Verzeichnis zu erstellen, dann löschen Sie das Verzeichnis von innen oder außen von Python, dann verwenden Sie mkpath Um das gleiche Verzeichnis erneut zu erstellen, mkpath wird einfach seine ungültige zwischengespeicherte Information verwenden, die das Verzeichnis zuvor erstellt hat, und wird das Verzeichnis tatsächlich nicht erneut erstellen. Im Gegensatz, os.makedirs verlässt sich nicht auf einen solchen Cache. Diese Einschränkung kann für einige Anwendungen in Ordnung sein.


In Bezug auf die Verzeichnisse ModusBitte beachten Sie die Dokumentation, wenn Sie sich darum kümmern.


809
2018-01-16 17:31



Mit try except und dem richtigen Fehlercode von errrno Modul wird die Race Condition beseitigt und ist plattformübergreifend:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

Mit anderen Worten, wir versuchen, die Verzeichnisse zu erstellen, aber wenn sie bereits existieren, ignorieren wir den Fehler. Auf der anderen Seite wird jeder andere Fehler gemeldet. Wenn Sie z. B. vorher dir 'a' erstellen und alle Berechtigungen daraus entfernen, erhalten Sie eine OSError angehoben mit errno.EACCES (Berechtigung verweigert, Fehler 13).


572
2018-02-17 17:17



Ich würde persönlich empfehlen, dass Sie verwenden os.path.isdir() statt zu testen os.path.exists().

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

Wenn Sie haben:

>>> dir = raw_input(":: ")

Und eine dumme Benutzereingabe:

:: /tmp/dirname/filename.etc

... Sie werden mit einem Verzeichnis namens enden filename.etc wenn Sie dieses Argument übergeben os.makedirs() wenn du mit testest os.path.exists().


85
2018-01-14 17:57



Prüfen os.makedirs: (Es stellt sicher, dass der vollständige Pfad existiert.)
 Um die Tatsache zu verarbeiten, dass das Verzeichnis möglicherweise vorhanden ist, fangen Sie OSError ab. (Wenn exist_ok False ist (der Standardwert), wird ein OSError ausgelöst, wenn das Zielverzeichnis bereits existiert.)

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass

56
2017-11-07 19:01



Einblicke in die Besonderheiten dieser Situation

Sie geben eine bestimmte Datei an einem bestimmten Pfad und Sie ziehen das Verzeichnis aus dem Dateipfad. Nachdem Sie sichergestellt haben, dass Sie das Verzeichnis haben, versuchen Sie, eine Datei zum Lesen zu öffnen. Um diesen Code zu kommentieren:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

Wir wollen vermeiden, dass die eingebaute Funktion überschrieben wird, dir. Ebenfalls, filepath oder vielleicht fullfilepath ist wahrscheinlich ein besserer semantischer Name als filename also wäre das besser geschrieben:

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

Ihr Endziel ist es, diese Datei zu öffnen, die Sie ursprünglich zum Schreiben angeben, aber Sie nähern sich im Wesentlichen diesem Ziel (basierend auf Ihrem Code) wie diesem, wodurch die Datei geöffnet wird lesen:

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

Annahme der Öffnung zum Lesen

Warum sollten Sie ein Verzeichnis für eine Datei erstellen, von der Sie erwartet haben, dass sie dort ist und in der Lage zu lesen?

Versuchen Sie einfach, die Datei zu öffnen.

with open(filepath) as my_file:
    do_stuff(my_file)

Wenn das Verzeichnis oder die Datei nicht vorhanden ist, erhalten Sie eine IOError mit einer zugehörigen Fehlernummer: errno.ENOENT zeigt unabhängig von Ihrer Plattform auf die richtige Fehlernummer. Sie können es fangen, wenn Sie möchten, zum Beispiel:

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

Vorausgesetzt, wir öffnen zum Schreiben

Das ist wahrscheinlich was du willst.

In diesem Fall stehen wir wahrscheinlich keinen Rassenbedingungen gegenüber. Also tu so, wie du warst, aber beachte, dass du zum Schreiben mit dem öffnen musst w Modus (oder a Anhängen). Es ist auch eine bewährte Vorgehensweise von Python, den Kontextmanager zum Öffnen von Dateien zu verwenden.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

Nehmen wir an, wir haben mehrere Python-Prozesse, die versuchen, alle ihre Daten in dasselbe Verzeichnis zu stellen. Dann können wir Streit über die Erstellung des Verzeichnisses haben. In diesem Fall ist es am besten, die makedirs Rufen Sie einen try-except-Block auf.

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

29
2018-01-22 23:49



Ausgehend von Python 3.5, pathlib.Path.mkdir hat ein exist_ok Flagge:

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

Dies erstellt rekursiv das Verzeichnis und löst keine Ausnahme aus, wenn das Verzeichnis bereits existiert.

(genauso wie os.makedirs bekam ein exists_ok Flagge beginnend mit Python 3.2).


28
2017-12-14 16:06