Relative Pfade in Python

244

Ich erstelle ein einfaches Hilfsskript für die Arbeit, das einige Vorlagendateien in unserer Codebasis in das aktuelle Verzeichnis kopiert. Ich habe jedoch nicht den absoluten Pfad zu dem Verzeichnis, in dem die Vorlagen gespeichert sind. Ich habe einen relativen Pfad vom Skript, aber wenn ich das Skript aufrufe, wird dieser als Pfad relativ zum aktuellen Arbeitsverzeichnis behandelt. Gibt es eine Möglichkeit anzugeben, dass diese relative URL stattdessen vom Speicherort des Skripts stammt?

Baudtack
quelle

Antworten:

324

In der Datei mit dem Skript möchten Sie Folgendes tun:

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

Dadurch erhalten Sie den absoluten Pfad zu der gesuchten Datei. Beachten Sie, dass Sie bei Verwendung von setuptools wahrscheinlich stattdessen die Paketressourcen- API verwenden sollten.

UPDATE : Ich antworte hier auf einen Kommentar, damit ich ein Codebeispiel einfügen kann. :-)

Habe ich Recht, __file__wenn ich denke, dass dies nicht immer verfügbar ist (z. B. wenn Sie die Datei direkt ausführen, anstatt sie zu importieren)?

Ich gehe davon aus, dass Sie das __main__Skript meinen, wenn Sie erwähnen, dass die Datei direkt ausgeführt wird. Wenn ja, scheint dies auf meinem System nicht der Fall zu sein (Python 2.5.1 unter OS X 10.5.7):

#foo.py
import os
print os.getcwd()
print __file__

#in the interactive interpreter
>>> import foo
/Users/jason
foo.py

#and finally, at the shell:
~ % python foo.py
/Users/jason
foo.py

Ich weiß jedoch, dass es einige Macken bei __file__C-Erweiterungen gibt. Zum Beispiel kann ich dies auf meinem Mac tun:

>>> import collections #note that collections is a C extension in Python 2.5
>>> collections.__file__
'/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-
dynload/collections.so'

Dies löst jedoch eine Ausnahme auf meinem Windows-Computer aus.

Jason Baker
quelle
1
Habe ich Recht, wenn ich denke, dass eine Datei nicht immer verfügbar ist (z. B. wenn Sie die Datei direkt ausführen, anstatt sie zu importieren)?
Stephen Edmonds
@ Stephen Edmonds Ich verwende eine Datei, die ich ausführe, anstatt sie zu importieren, und sie funktioniert hervorragend.
Baudtack
22
Beachten Sie, dass Sie os.path.join überall für die Portabilität verwenden sollten:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
Ford
22
os.path.dirname(__file__)kann eine leere Zeichenfolge geben, verwenden Sie os.path.dirname(os.path.abspath(__file__))stattdessen
Dmitry Trofimov
14
Es ist eine Kleinigkeit, aber BITTE verwenden Sie dir nicht als Variablennamen, da es ein eingebauter Name ist.
David
63

Sie benötigen os.path.realpath(Beispiel unten fügt das übergeordnete Verzeichnis zu Ihrem Pfad hinzu)

import sys,os
sys.path.append(os.path.realpath('..'))
user989762
quelle
2
os.path.dirname(__file__)gab mir eine leere Schnur. Das hat perfekt funktioniert.
Darragh Enright
3
Dies scheint das übergeordnete Verzeichnis des Verzeichnisses anzugeben, in dem das Skript ausgeführt wird, und nicht den Speicherort des Skripts.
Coquelicot
10
os.path.realpath('..')gibt Ihnen das übergeordnete Verzeichnis des aktuellen Arbeitsverzeichnisses . Das ist normalerweise nicht das, was du willst.
Martijn Pieters
1
@DarraghEnright: Das passiert nur in einer Python-Script-to-Exe-Verpackungsumgebung. Dies ist eine der seltenen Ausnahmen, bei denen es die Alternative wäre, sich auf das aktuelle Arbeitsverzeichnis zu verlassen.
Martijn Pieters
52

Wie in der akzeptierten Antwort erwähnt

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, '/relative/path/to/file/you/want')

Ich möchte das nur hinzufügen

Die letztere Zeichenfolge kann nicht mit dem Backslash beginnen. Tatsächlich sollte keine Zeichenfolge einen Backslash enthalten

Es sollte so etwas wie sein

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')

Die akzeptierte Antwort kann in einigen Fällen irreführend sein. Weitere Informationen finden Sie unter diesem Link

Ahmed
quelle
4
Ja, die Verwendung os.path.joinist besser, da sie mit dem betriebssystemspezifischen Trennzeichen verbunden werden.
Farshid T
'/relative/path...'ist kein relativer Pfad. Ist das beabsichtigt?
Steveve
Diese Antwort ist jetzt veraltet, da die oberste Antwort so bearbeitet wurde, dass ein korrekter relativer Pfad verwendet wird os.path.join(). Was bleibt, ist die Präferenz, separate Zeichenfolgen für jedes Pfadelement zu verwenden, anstatt das Pfadtrennzeichen fest zu codieren.
Martijn Pieters
@MartijnPieters Ja, die Top-Antwort wurde teilweise so bearbeitet, dass sie übereinstimmt. Die getrennten Zeichenfolgen sind jedoch keine Präferenz. Wenn Sie die Stiche so trennen, ist sie os-unabhängig.
jshrimp29
26

Es ist jetzt 2018 und Python hat sich bereits vor __future__langer Zeit weiterentwickelt. So wie über die erstaunliche Verwendung pathlibmit Python 3.4 kommt die Aufgabe zu erfüllen , anstatt mit zu kämpfen os, os.path, glob, shutiletc.

Wir haben hier also 3 Pfade (möglicherweise dupliziert):

  • mod_path: Dies ist der Pfad des einfachen Hilfsskripts
  • src_path: enthält einige Vorlagendateien, die darauf warten, kopiert zu werden.
  • cwd: aktuelles Verzeichnis , das Ziel dieser Vorlagendateien.

und das Problem ist: Wir haben nicht den vollständigen Pfad von src_path, wissen nur, dass es der relative Pfad zum ist mod_path.

Lassen Sie uns dies nun mit dem Erstaunlichen lösen pathlib:

# Hope you don't be imprisoned by legacy Python code :)
from pathlib import Path

# `cwd`: current directory is straightforward
cwd = Path.cwd()

# `mod_path`: According to the accepted answer and combine with future power
# if we are in the `helper_script.py`
mod_path = Path(__file__).parent
# OR if we are `import helper_script`
mod_path = Path(helper_script.__file__).parent

# `src_path`: with the future power, it's just so straightforward
relative_path_1 = 'same/parent/with/helper/script/'
relative_path_2 = '../../or/any/level/up/'
src_path_1 = (mod_path / relative_path_1).resolve()
src_path_2 = (mod_path / relative_path_2).resolve()

In Zukunft ist es so einfach. : D.


Darüber hinaus können wir diese Vorlagendateien auswählen, überprüfen und kopieren / verschieben mit pathlib:

if src_path != cwd:
    # When we have different types of files in the `src_path`
    for template_path in src_path.glob('*.ini'):
        fname = template_path.name
        target = cwd / fname
        if not target.exists():
            # This is the COPY action
            with target.open(mode='wb') as fd:
                fd.write(template_path.read_bytes())
            # If we want MOVE action, we could use:
            # template_path.replace(target)
YaOzI
quelle
14

Betrachten Sie meinen Code:

import os


def readFile(filename):
    filehandle = open(filename)
    print filehandle.read()
    filehandle.close()



fileDir = os.path.dirname(os.path.realpath('__file__'))
print fileDir

#For accessing the file in the same folder
filename = "same.txt"
readFile(filename)

#For accessing the file in a folder contained in the current folder
filename = os.path.join(fileDir, 'Folder1.1/same.txt')
readFile(filename)

#For accessing the file in the parent folder of the current folder
filename = os.path.join(fileDir, '../same.txt')
readFile(filename)

#For accessing the file inside a sibling folder.
filename = os.path.join(fileDir, '../Folder2/same.txt')
filename = os.path.abspath(os.path.realpath(filename))
print filename
readFile(filename)
Fahad Haleem
quelle
Wenn ich dies in Windows ausführe, erhalte ich eine Fehlermeldung: FileNotFoundError: [Errno 2] Keine solche Datei oder kein solches Verzeichnis: '<Pfad>', wobei <Pfad> die richtigen Pfadsegmente hat, aber \\ für Trennzeichen verwendet.
Lonstar
11

Siehe sys.path Wie beim Programmstart initialisiert, ist das erste Element dieser Liste, Pfad [0], das Verzeichnis, das das Skript enthält, mit dem der Python-Interpreter aufgerufen wurde.

Verwenden Sie diesen Pfad als Stammordner, aus dem Sie Ihren relativen Pfad anwenden

>>> import sys
>>> import os.path
>>> sys.path[0]
'C:\\Python25\\Lib\\idlelib'
>>> os.path.relpath(sys.path[0], "path_to_libs") # if you have python 2.6
>>> os.path.join(sys.path[0], "path_to_libs")
'C:\\Python25\\Lib\\idlelib\\path_to_libs'
Tom Leys
quelle
3
Das ist nicht unbedingt wahr. Normalerweise ist sys.path [0] eine leere Zeichenfolge oder ein Punkt, der ein relativer Pfad zum aktuellen Verzeichnis ist. Wenn Sie das aktuelle Verzeichnis möchten, verwenden Sie os.getcwd.
Jason Baker
Das Originalplakat kommentierte, dass das aktuelle Arbeitsverzeichnis der falsche Ort ist, von dem aus der relative Pfad erstellt werden kann. Sie sagen zu Recht, dass sys.path [0] nicht immer gültig ist.
Tom Leys
Nein, sys.path[0]wird nicht immer auf das übergeordnete Verzeichnis gesetzt. Python-Code kann mit -coder -müber einen eingebetteten Interpreter aufgerufen werden. An diesem Punkt sys.path[0]wird ein ganz anderer Wert festgelegt.
Martijn Pieters
6

Anstatt zu verwenden

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

Wie in der akzeptierten Antwort wäre es robuster zu verwenden:

import inspect
import os
dirname = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

Da die Verwendung von __file__ die Datei zurückgibt, aus der das Modul geladen wurde, wenn es aus einer Datei geladen wurde. Wenn also die Datei mit dem Skript von einer anderen Stelle aufgerufen wird, ist das zurückgegebene Verzeichnis nicht korrekt.

Diese Antworten enthalten weitere Details: https://stackoverflow.com/a/31867043/5542253 und https://stackoverflow.com/a/50502/5542253

Smiley
quelle
5
inspect.stack()ist eine teure Funktion zum Aufrufen. Es ruft Informationen für alle Stapelrahmen ab, die Sie dann verwerfen und nur den obersten für erhalten. Grundsätzlich wird inspect.getfile()das Modulobjekt aufgerufen, das gerade zurückgegeben wird module.__file__. Sie sind viel besser darin, nur zu verwenden __file__.
Martijn Pieters
4

Hallo, zuerst sollten Sie die Funktionen os.path.abspath (Pfad) und os.path.relpath (Pfad) verstehen.

Kurz gesagt, os.path.abspath (Pfad) erstellt einen relativen Pfad zum absoluten Pfad . Und wenn der angegebene Pfad selbst ein absoluter Pfad ist, gibt die Funktion denselben Pfad zurück.

In ähnlicher Weise erstellt os.path.relpath (Pfad) einen absoluten Pfad zum relativen Pfad . Wenn der angegebene Pfad selbst ein relativer Pfad ist, gibt die Funktion denselben Pfad zurück.

Mit dem folgenden Beispiel können Sie das obige Konzept richtig verstehen :

Angenommen, ich habe eine Datei input_file_list.txt, die eine Liste der Eingabedateien enthält, die von meinem Python-Skript verarbeitet werden sollen.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Wenn Sie über Ordnerstruktur sehen, input_file_list.txt in Gegenwart ist Copyofconc Ordner und Dateien , die von dem Python - Skript verarbeitet werden sollen , die in Konz Ordner

Der Inhalt der Datei input_file_list.txt ist jedoch wie folgt :

.. \ conc \ input1.dic

.. \ conc \ input2.dic

Und mein Python-Skript ist in Laufwerk D: vorhanden .

Der in der Datei input_file_list.txt angegebene relative Pfad ist relativ zum Pfad der Datei input_file_list.txt .

Wenn das Python-Skript das aktuelle Arbeitsverzeichnis ausführen soll (verwenden Sie os.getcwd () , um den Pfad abzurufen ).

Da mein relativer Pfad ist relativ zu input_file_list.txt , das heißt „D: \ Copyofconc“ , ich habe das aktuelle Arbeitsverzeichnis ändern „D: \ Copyofconc“ .

Also muss ich os.chdir ('D: \ Copyofconc') verwenden , damit das aktuelle Arbeitsverzeichnis "D: \ Copyofconc" ist .

Um nun die Dateien input1.dic und input2.dic zu erhalten , lese ich die Zeilen ".. \ conc \ input1.dic" und verwende dann den Befehl

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (um den relativen Pfad in den absoluten Pfad zu ändern. Hier lautet das aktuelle Arbeitsverzeichnis "D: \ Copyofconc", die Datei ". \ conc \ input1. Auf dic "wird relativ zu" D: \ Copyofconc "zugegriffen.

so input1_path wird "D: \ Konz \ input1.dic"

Chandrajyoti Das
quelle
4

Dieser Code gibt den absoluten Pfad zum Hauptskript zurück.

import os
def whereAmI():
    return os.path.dirname(os.path.realpath(__import__("__main__").__file__))

Dies funktioniert auch in einem Modul.

BookOwl
quelle
Anstatt erneut zu importieren, würden Sie verwenden sys.modules['__main__'].__file__.
Martijn Pieters
3

Eine Alternative, die für mich funktioniert:

this_dir = os.path.dirname(__file__) 
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))
J0hnG4lt
quelle
0

Was für mich funktioniert hat, ist mit sys.path.insert. Dann gab ich das Verzeichnis an, in das ich gehen musste. Zum Beispiel musste ich nur ein Verzeichnis hochgehen.

import sys
sys.path.insert(0, '../')
Weiße Katze
quelle
1
Dies hängt vom aktuellen Arbeitsverzeichnis ab, das sich grundlegend von dem unterscheiden kann, was Sie tatsächlich möchten.
Martijn Pieters
-2

Ich bin nicht sicher, ob dies für einige der älteren Versionen gilt, aber ich glaube, dass Python 3.3 native Unterstützung für relative Pfade bietet.

Der folgende Code sollte beispielsweise eine Textdatei im selben Ordner wie das Python-Skript erstellen:

open("text_file_name.txt", "w+t")

(Beachten Sie, dass es am Anfang keinen Vorwärts- oder Rückwärtsschlag geben sollte, wenn es sich um einen relativen Pfad handelt.)

Samy Bencherif
quelle
Richtig, das wird also vom CWD aus funktionieren, was das OP nicht verlangt. Sie möchten vom Speicherort des Skripts aus arbeiten.
Samy Bencherif