Verweisen auf relative Ressourcenpfade bei der Arbeit mit einem Code-Repository

187

Wir arbeiten mit einem Code-Repository, das sowohl unter Windows als auch unter Linux bereitgestellt wird - manchmal in verschiedenen Verzeichnissen. Wie sollte eines der Module im Projekt auf eine der Nicht-Python-Ressourcen im Projekt verweisen (CSV-Dateien usw.)?

Wenn wir so etwas machen wie:

thefile=open('test.csv')

oder:

thefile=open('../somedirectory/test.csv')

Es funktioniert nur, wenn das Skript aus einem bestimmten Verzeichnis oder einer Teilmenge der Verzeichnisse ausgeführt wird.

Was ich gerne machen würde, ist so etwas wie:

path=getBasePathOfProject()+'/somedirectory/test.csv'
thefile=open(path)

Ist es möglich?

Olamundo
quelle

Antworten:

255

Versuchen Sie, einen Dateinamen relativ zum aktuellen Dateipfad zu verwenden. Beispiel für './my_file':

fn = os.path.join(os.path.dirname(__file__), 'my_file')

In Python 3.4+ können Sie auch pathlib verwenden :

fn = pathlib.Path(__file__).parent / 'my_file'
c089
quelle
3
Ich denke, diese Lösung funktioniert nur, wenn sich die Ressource im selben Verzeichnis der Python-Datei oder in einem Unterverzeichnis davon befindet. Wie lösen Sie es, wenn Sie die folgende Baumstruktur haben: / Project_Root_dir / python_files_dir / Einige weitere Unterverzeichnisse hier py_file.py / resources / einige Unterverzeichnisse hier resource_file.csv
olamundo
1
Entschuldigung, der Dateibaum wurde bei dieser letzten Nachricht verstümmelt ... zweiter Versuch: Sie haben Ihre Datei unter /Project_Root_dir/python_files_dir/some_subdirs/py_file.py und Ihre Ressourcendatei unter /Project_Root_dir/resources/some_subdirs/resource_file.csv
Olamundo
28
Sie sollten in der Lage sein, mit join (foo, '..') zum übergeordneten Verzeichnis zu gelangen. Verwenden Sie also aus / root / python_files / module / myfile os.path.join (os.path.dirname ( __file__), '..', '..', 'resources')
c089
7
os.pardirist etwas besser als '..', obwohl die beiden unter POSIX und Windows gleichwertig sind.
Davidchambers
4
@cedbeu: Es ist auf jedem System, auf das ich jemals gestoßen bin, gleichwertig und ich denke, auf jedem System läuft Python heute (bitte korrigieren Sie mich, wenn ich hier falsch liege). Wenn Sie jedoch erwarten, dass Python in Zukunft mit einem anderen Pfadtrennzeichen auf ein System portiert wird und Ihr Code dafür bereit sein soll, ist os.pardir portabler. Ich würde sagen, dass jeder Programmierer, selbst einer, der nie eine Python gelesen hat, die Bedeutung von ".." kennt, während "os.pardir" eine Indirektionsebene ist, die man in der Dokumentation so persönlich nachschlagen müsste. d bleibe bei "..".
c089
40

Wenn Sie Setup-Tools verwenden oder verteilen (eine setup.py-Installation), scheint der "richtige" Weg, auf diese gepackten Ressourcen zuzugreifen, package_resources zu verwenden.

In Ihrem Fall wäre das Beispiel

import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")

Was natürlich die Ressource und die gelesenen Binärdaten liest, wäre der Wert von my_data

Wenn Sie nur den Dateinamen benötigen, können Sie auch verwenden

resource_filename(package_or_requirement, resource_name)

Beispiel:

resource_filename("MyPackage","foo.dat")

Der Vorteil ist, dass es garantiert funktioniert, auch wenn es sich um eine Archivverteilung wie ein Ei handelt.

Siehe http://packages.python.org/distribute/pkg_resources.html#resourcemanager-api

Sharoon Thomas
quelle
3
Ich weiß, dass dies eine alte Antwort ist. Meine bevorzugte Methode ist (/ war vielleicht?) Die Verwendung von pkg_resources, aber gibt es beim Verschwinden von Eiern mit Reißverschluss irgendeinen Schaden, wenn man sie __file__wie in guten alten Zeiten verwendet?
Pykler
1
Dies ist ein solider Ansatz. Selbst wenn die Eierkonvention wegfällt, gibt es keine Setuptools und viele installieren immer noch Deps gegen Git-Repos, bei denen das Ei zur Laufzeit erstellt wird
deepelement
18

In Python beziehen sich die Pfade auf das aktuelle Arbeitsverzeichnis , in den meisten Fällen auf das Verzeichnis, in dem Sie Ihr Programm ausführen. Das aktuelle Arbeitsverzeichnis ist sehr wahrscheinlich nicht dasselbe wie das Verzeichnis Ihrer Moduldatei. Daher ist die Verwendung eines Pfads relativ zu Ihrer aktuellen Moduldatei immer eine schlechte Wahl.

Die Verwendung des absoluten Pfades sollte die beste Lösung sein:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
himmelfrei
quelle
15

Ich benutze oft etwas Ähnliches:

import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))

# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir') 

pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
    f = open(pathjoin(DATA_DIR, fn))
    # ...

Die Variable

__file__

Enthält den Dateinamen des Skripts, in das Sie diesen Code schreiben, sodass Sie Pfade relativ zum Skript erstellen können, die jedoch weiterhin mit absoluten Pfaden geschrieben sind. Es funktioniert aus mehreren Gründen recht gut:

  • Pfad ist absolut, aber immer noch relativ
  • Das Projekt kann weiterhin in einem relativen Container bereitgestellt werden

Sie müssen jedoch auf Plattformkompatibilität achten - Windows os.pathsep unterscheidet sich von UNIX.

user137673
quelle
4
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

Sie versuchen auch, Ihre cwdVerwendung zu normalisieren os.path.abspath(os.getcwd()). Mehr Infos hier .

Gavoja
quelle
3
Sehr wenige Anwendungsfälle, in denen der cwdPfad eines Moduls ist
cedbeu
Es funktioniert nicht in einem Paket, sondern nur aus demselben Verzeichnis (oder Arbeitsverzeichnis), das vom Skript festgelegt wurde.
Alexandra
Dies funktioniert nicht, wenn der Benutzer das Programm unter Verwendung des absoluten Pfads aus einem anderen Verzeichnis ausführt. zB python3 /usr/someone/test.py
sgrpwr
2

Sie können die eingebaute __file__Variable verwenden. Es enthält den Pfad der aktuellen Datei. Ich würde getBaseOfProject in einem Modul im Stammverzeichnis Ihres Projekts implementieren. Dort würde ich den Wegteil bekommen __file__und das zurückgeben. Diese Methode kann dann überall in Ihrem Projekt verwendet werden.

Achim
quelle
0

Ich war hier ein bisschen ratlos. Wollte einige Ressourcendateien in eine Raddatei packen und darauf zugreifen. Hat die Verpackung die Manifestdatei verwendet, aber pip install hat sie nur installiert, wenn es sich um ein Unterverzeichnis handelt. In der Hoffnung, dass diese Szenenaufnahmen helfen

├── cnn_client
   ├── image_preprocessor.py
   ├── __init__.py
   ├── resources
      ├── mscoco_complete_label_map.pbtxt
      ├── retinanet_complete_label_map.pbtxt
      └── retinanet_label_map.py
   ├── tf_client.py

MANIFEST.in

recursive-include cnn_client/resources *

Erstellt ein Weel mit Standard setup.py. pip hat die Raddatei installiert. Überprüfen Sie nach der Installation, ob Ressourcen installiert sind. Sie sind

ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources

mscoco_complete_label_map.pbtxt
retinanet_complete_label_map.pbtxt 
 retinanet_label_map.py  

In tfclient.py, um auf diese Dateien zuzugreifen. von

templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
 file_path = os.path.join(templates_dir, \
            'mscoco_complete_label_map.pbtxt')
        s = open(file_path, 'r').read()

Und es funktioniert.

Alex Punnen
quelle
-5

Ich habe lange Zeit damit verbracht, die Antwort darauf herauszufinden, aber ich habe sie endlich verstanden (und es ist eigentlich ganz einfach):

import sys
import os
sys.path.append(os.getcwd() + '/your/subfolder/of/choice')

# now import whatever other modules you want, both the standard ones,
# as the ones supplied in your subfolders

Dadurch wird der relative Pfad Ihres Unterordners an die Verzeichnisse angehängt, in denen Python angezeigt werden soll. Es ist ziemlich schnell und schmutzig, aber es funktioniert wie ein Zauber :)

Rutger Semp
quelle
6
Dies funktioniert nur, wenn Sie das Python-Programm aus demselben Verzeichnis wie die betreffende .py-Datei ausführen. Und in diesem Fall könnten Sie es open('your/subfolder/of/choice')trotzdem tun .
Paul Fisher
4
und das OP erwähnte, dass der Code sowohl unter Windows als auch unter Linux funktionieren muss. Das wird nicht.
user183037