Tutorial

Verwenden des pathlib-Moduls zum Bearbeiten von Dateisystempfaden in Python 3

PythonDevelopment

Der Autor hat den COVID-19 Relief Fund dazu ausgewählt, eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

Einführung

Python 3 enthält das pathlib-Modul zur betriebssystemunabhängigen Bearbeitung von Dateisystempfaden. pathlib ähnelt dem os.path-Modul; pathlib bietet jedoch eine übergeordnete – und oft bequemere – Oberfläche als os.path.

Wir können Dateien auf einem Computer mit hierarchischen Pfaden identifizieren. Beispielsweise können wir die Datei wave.txt auf einem Computer mit diesem Pfad identifizieren: /Users/sammy/ocean/wave.txt. Betriebssysteme stellen Pfade etwas anders dar. Windows kann den Pfad zur Datei wave.txt folgendermaßen darstellen: C:\Users\sammy\ocean\wave.txt.

Sie finden das pathlib-Modul ggf. nützlich, wenn Sie in Ihrem Python-Programm Dateien im Dateisystem erstellen oder verschieben, Dateien im Dateisystem auflisten, die alle einer bestimmten Erweiterung oder einem Muster entsprechen, oder basierend auf Sammlungen roher Zeichenfolgen dem jeweiligen Betriebssystem entsprechende Dateipfade erstellen. Zwar ließen sich auch andere Tools (wie das Modul os.path) zur Erledigung vieler dieser Aufgaben verwenden, doch können Sie mit dem pathlib-Modul solche Operationen mit hoher Lesbarkeit und einer minimalen Codemenge ausführen.

In diesem Tutorial sehen wir uns einige der Wege an, um das Modul pathlib zum Darstellen und Bearbeiten von Dateisystempfaden zu verwenden.

Voraussetzungen

Um das Beste aus diesem Tutorial herauszuholen, empfiehlt es sich, eine gewisse Vertrautheit mit Programmierung in Python 3 aufzuweisen. Sie können sich für die erforderlichen Hintergrundinformationen folgende Tutorials ansehen:

Erstellen von Path-Instanzen

Das pathlib-Modul bietet mehrere Klassen; eine der wichtigsten ist jedoch die Path-Klasse. Instanzen der Path-Klasse stellen einen Pfad zu einer Datei oder einem Verzeichnis im Dateisystem unseres Computers dar.

Beispielsweise instanziiert der folgende Code eine Path-Instanz, die einen Teil des Pfads zu einer Datei wave.txt darstellt:

from pathlib import Path

wave = Path("ocean", "wave.txt")
print(wave)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
ocean/wave.txt

from pathlib import Path macht die Path-Klasse für unser Programm verfügbar. Dann instanziiert Path("ocean", "wave.txt") eine neue Path-Instanz. Ein Drucken der Ausgabe zeigt, dass Python das entsprechende Betriebssystemtrennzeichen / zwischen den beiden Pfadkomponenten hinzugefügt hat, die wir angegeben haben: "ocean" und "wave.txt".

Anmerkung: Je nach Betriebssystem kann Ihre Ausgabe von den Beispielausgaben in diesem Tutorial leicht abweichen. Wenn Sie Windows ausführen, könnte Ihre Ausgabe für dieses erste Beispiel so aussehen: ocean\wave.txt.

Aktuell enthält das Path-Objekt, das der Variable wave zugeordnet ist, einen relative path (relativen Pfad). Anders gesagt kann ocean/wave.txt an mehreren Stellen in unserem Dateisystem vorkommen. Beispielsweise kann die Datei in /Users/user_1/ocean/wave.txt oder /Users/user_2/research/ocean/wave.txt vorhanden sein; wir haben jedoch noch nicht genau angegeben, worauf wir uns beziehen. Ein absoluter Pfad dagegen verweist eindeutig auf einen Speicherort im Dateisystem.

Mit Path.home() können Sie den absoluten Pfad zum Stammverzeichnis des aktuellen Benutzers abrufen:

home = Path.home()
wave_absolute = Path(home, "ocean", "wave.txt")
print(home)
print(wave_absolute)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe, die der folgenden ähnelt:

Output
/Users/sammy /Users/sammy/ocean/wave.txt

Anmerkung: Wie zuvor erwähnt, wird Ihre Ausgabe je nach Betriebssystem variieren. Auch Ihr Stammverzeichnis wird sich natürlich von /Users/sammy unterscheiden.

Path.home() gibt eine Path-Instanz mit einem absoluten Pfad zum Stammverzeichnis des aktuellen Benutzers zurück. Dann übergeben wir diese Path-Instanz und die Zeichenfolgen "ocean" und "wave.txt" in einen anderen Path-Konstruktor, um einen absoluten Pfad zur Datei wave.txt zu erstellen. Die Ausgabe zeigt, dass die erste Zeile das Stammverzeichnis ist und die zweite Zeile das Stammverzeichnis plus ocean/wave.txt ist.

Dieses Beispiel veranschaulicht auch ein wichtiges Merkmal der Path-Klasse: Der Path-Konstruktor akzeptiert sowohl Zeichenfolgen als auch bereits vorhandene Path-Objekte.

Sehen wir uns die Unterstützung für beide Zeichenfolgen und Path-Objekte im Path-Konstruktor etwas genauer an:

shark = Path(Path.home(), "ocean", "animals", Path("fish", "shark.txt"))
print(shark)

Wenn wir diesen Python-Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
/Users/sammy/ocean/animals/fish/shark.txt

shark ist ein Path (Pfad) zu einer Datei, die wir sowohl mit beiden Path-Objekten (Path.home() und Path("fish", "shark.txt")) als auch Zeichenfolgen ("ocean" und "animals") erstellt haben. Der Path-Konstruktor behandelt beide Arten von Objekten auf intelligente Weise und verknüpft sie sauber mit dem entsprechenden Betriebssystemtrennzeichen, in dem Fall /.

Zugriff auf Dateiattribute

Nachdem wir die Path-Instanzen erstellt haben, erfahren Sie nun, wie Sie diese Instanzen zum Zugriff auf Informationen über eine Datei verwenden können.

Wir können die Attribute name und suffix zum Zugreifen auf Dateinamen und Dateiendungen nutzen:

wave = Path("ocean", "wave.txt")
print(wave)
print(wave.name)
print(wave.suffix)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
/Users/sammy/ocean/wave.txt wave.txt .txt

Diese Ausgabe zeigt, dass der Name der Datei am Ende unseres Pfades wave.txt ist und das Suffix dieser Datei .txt lautet.

Path-Instanzen bieten außerdem die Funktion with_name, mit der Sie nahtlos ein neues Path-Objekt mit einem anderen Namen erstellen können:

wave = Path("ocean", "wave.txt")
tides = wave.with_name("tides.txt")
print(wave)
print(tides)

Wenn wir dies ausführen, erhalten wir eine Ausgabe wie die folgende:

ocean/wave.txt
ocean/tides.txt

Der Code erstellt zunächst eine Path-Instanz, die auf eine Datei namens wave.txt verweist. Dann rufen wir die Methode with_name für wave auf, um eine zweite Path-Instanz zurückzugeben, die auf eine neue Datei namens tides.txt verweist. Der Verzeichnisteil ocean/ des Pfads bleibt unverändert, sodass der endgültige Pfad ocean/tides.txt lautet.

Zugriff auf Vorgänger

Manchmal ist es nützlich, Verzeichnisse aufzurufen, die einen bestimmten Pfad enthalten. Betrachten wir ein Beispiel:

shark = Path("ocean", "animals", "fish", "shark.txt")
print(shark)
print(shark.parent)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe, die der folgenden ähnelt:

Output
ocean/animals/fish/shark.txt ocean/animals/fish

Das Attribut parent bei einer Path-Instanz gibt den unmittelbarsten Vorgänger eines bestimmten Dateipfads zurück. In diesem Fall wird das Verzeichnis zurückgegeben, das die Datei shark.txt enthält: ocean/animals/fish.

Wir können auf das Attribut parent mehrmals hintereinander zugreifen, um die Vorgängerstruktur einer bestimmten Datei nach oben zu durchlaufen:

shark = Path("ocean", "animals", "fish", "shark.txt")
print(shark)
print(shark.parent.parent)

Wenn wir diesen Code ausführen, erhalten wir die folgende Ausgabe:

Output
ocean/animals/fish/shark.txt ocean/animals

Die Ausgabe ähnelt der früheren Ausgabe, doch befinden wir uns nun noch eine Stufe höher, da wir .parent ein zweites Mal aufgerufen haben. Zwei Verzeichnisse nach oben von shark.txt befindet sich das Verzeichnis ocean/animals.

Verwenden von Glob zum Auflisten von Dateien

Es ist auch möglich, die Path-Klasse zum Auflisten von Dateien mit der Methode glob zu verwenden.

Gehen wir davon aus, wir haben eine Verzeichnisstruktur, die so aussieht:

└── ocean
    ├── animals
    │   └── fish
    │       └── shark.txt
    ├── tides.txt
    └── wave.txt

Ein Verzeichnis ocean enthält die Dateien tides.txt und wave.txt. Wir haben eine Datei namens shark.txt, die unter dem Verzeichnis ocean, einem Verzeichnis animals und einem Verzeichnis fish geschachtelt ist: ocean/animals/fish.

Um alle .txt-Dateien im Verzeichnis ocean aufzulisten, können wir sagen:

for txt_path in Path("ocean").glob("*.txt"):
    print(txt_path)

Dieser Code würde eine Ausgabe ergeben wie:

Output
ocean/wave.txt ocean/tides.txt

Das glob-Muster __"*.txt" findet alle Dateien, die auf .txt enden. Da das Codebeispiel dieses Glob im Verzeichnis ocean ausführt, werden die beiden .txt-Dateien im Verzeichnis ocean zurückgegeben: wave.txt und tides.txt.

Anmerkung: Wenn Sie die in diesem Beispiel angegebenen Ausgaben replizieren möchten, müssen Sie die hier dargestellte Verzeichnisstruktur auf Ihrem Computer nachahmen.

Wir können die glob-Methode auch rekursiv verwenden. Um alle .txt-Dateien im Verzeichnis ocean und allen seinen Unterverzeichnissen aufzulisten, können wir sagen:

for txt_path in Path("ocean").glob("**/*.txt"):
    print(txt_path)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
ocean/wave.txt ocean/tides.txt ocean/animals/fish/shark.txt

Der **-Teil des glob-Musters wird dieses Verzeichnis und alle Verzeichnisse darunter rekursiv abgleichen. Wir erhalten also nicht nur die Dateien wave.txt und tides.txt in der Ausgabe, sondern auch die Datei shark.txt, die unter ocean/animals/fish geschachtelt war.

Berechnung relativer Pfade

Wir können die Methode Path.relative_to verwenden, um Pfade in Relation zueinander zu berechnen. Die relative_to-Methode ist zum Beispiel nützlich, wenn Sie einen Teil eines langen Dateipfads abrufen möchten.

Erwägen Sie folgenden Code:

shark = Path("ocean", "animals", "fish", "shark.txt")
below_ocean = shark.relative_to(Path("ocean"))
below_animals = shark.relative_to(Path("ocean", "animals"))
print(shark)
print(below_ocean)
print(below_animals)

Wenn wir dies ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
ocean/animals/fish/shark.txt animals/fish/shark.txt fish/shark.txt

Die relative_to-Methode gibt ein neues Path-Objekt in Relation zum angegebenen Argument zurück. In unserem Beispiel berechnen wir den Pfad zu shark.txt in Relation zum Verzeichnis ocean und dann in Relation zu den Verzeichnissen ocean und animals.

Wenn relative_to keine Antwort berechnen kann, da wir ihm einen nicht zugeordneten Pfad geben, wird ein ValueError ausgelöst:

shark = Path("ocean", "animals", "fish", "shark.txt")
shark.relative_to(Path("unrelated", "path"))

Wir erhalten eine ValueError-Ausnahme, die von dem Code ausgelöst wurde und ungefähr so aussehen wird:

Output
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/Python3.8/pathlib.py", line 899, in relative_to raise ValueError("{!r} does not start with {!r}" ValueError: 'ocean/animals/fish/shark.txt' does not start with 'unrelated/path'

unrelated/path ist kein Teil von ocean/animals/fish/shark.txt, sodass Python keinen relativen Pfad für uns berechnen kann.

Zusammenfassung

Das pathlib-Modul ist eine leistungsfähige Komponente der Python-Standardbibliothek, mit der wir Dateisystempfade bei jedem Betriebssystem schnell bearbeiten können. In diesem Tutorial haben wir gelernt, einige der wichtigsten Dienstprogramme von pathlib zum Aufrufen von Dateiattributen, zum Auflisten von Dateien mit glob-Mustern und Durchlaufen von übergeordneten Dateien und Verzeichnissen zu verwenden.

Das pathlib-Modul macht zusätzliche Klassen und Dienstprogramme verfügbar, die wir in diesem Tutorial nicht abgedeckt haben. Nachdem Sie nun über Grundkenntnisse verfügen, können Sie die Dokumentation des pathlib-Moduls nutzen, um mehr über andere verfügbare Klassen und Dienstprogramme zu erfahren.

Wenn Sie daran interessiert sind, andere Python-Bibliotheken zu verwenden, sehen Sie sich die folgenden Tutorials an:

Creative Commons License