Ich habe ein Python-Projekt mit einer Konfigurationsdatei im Projektstamm. Auf die Konfigurationsdatei muss während des gesamten Projekts in einigen verschiedenen Dateien zugegriffen werden.
Es sieht also ungefähr so aus : <ROOT>/configuration.conf
<ROOT>/A/a.py
, <ROOT>/A/B/b.py
(wenn b, a.py auf die Konfigurationsdatei zugreifen).
Was ist der beste / einfachste Weg, um den Pfad zum Projektstamm und zur Konfigurationsdatei zu erhalten, ohne davon abhängig zu sein, in welcher Datei sich das Projekt befindet, in dem ich mich befinde? dh ohne zu benutzen ../../
? Es ist in Ordnung anzunehmen, dass wir den Namen des Projektstamms kennen.
<ROOT>/__init__.py
existieren?os.path.expanduser('~/.myproject/myproject.conf')
. Es funktioniert unter Unix und Windows.Antworten:
Sie können dies so tun, wie Django es tut: Definieren Sie eine Variable für den Projektstamm aus einer Datei, die sich in der obersten Ebene des Projekts befindet. Wenn Ihre Projektstruktur beispielsweise so aussieht:
In können
definitions.py
Sie definieren (dies erfordertimport os
):Mit dem bekannten Projektstamm können Sie also eine Variable erstellen, die auf den Speicherort der Konfiguration verweist (dies kann überall definiert werden, aber ein logischer Ort wäre, sie an einem Speicherort zu platzieren, an dem Konstanten definiert sind - z. B.
definitions.py
):Anschließend können Sie mit der import-Anweisung (z . B. in
utils.py
) einfach auf die Konstante (in einer der anderen Dateien) zugreifen :from definitions import CONFIG_PATH
.quelle
__init__.py
Datei zum Stammprojektverzeichnis hinzufügen, um die Datei definition.py so einzuschließen ? Sollte das richtig sein? Ich habe gerade mit Python angefangen und bin mir nicht sicher, welche Best Practices es gibt. Vielen Dank.__init__.py
wird nicht benötigt, da diese Datei nur beim Definieren von Paketen erforderlich ist: Die__init__.py
Dateien sind erforderlich, damit Python die Verzeichnisse als Pakete enthaltend behandelt. Auf diese Weise wird verhindert, dass Verzeichnisse mit einem allgemeinen Namen, z. B. eine Zeichenfolge, unbeabsichtigt gültige Module verbergen, die später im Modul-Suchpfad auftreten. Im einfachsten Fall__init__.py
kann es sich nur um eine leere Datei handeln, es kann jedoch auch der Initialisierungscode für das Paket ausgeführt oder die__all__
später beschriebene Variable festgelegt werden. Siehe: docs.python.org/3/tutorial/modules.html#packages__init.py__
. Es würde das Erstellen einer weiteren Datei speichern und die schönere Syntax von zulassenfrom root_pack import ROOT_DIR, CONFIG_PATH
.__init__.py
leer bleiben , aber das ist nicht unbedingt wahr (es ist schließlich eine Konvention). Weitere Informationen findenos.path.abspath
ruft in dem von Ihnen zitierten Beispiel einen String auf ,'__file__'
. Denken Sie daran, dass dies__file__
tatsächlich ein Importattribut ist, das für Python-Module definiert ist. In diesem Fall__file__
wird der Pfadname zurückgegeben, von dem das Modul geladen wird. Lesen Sie hier mehr (siehe Abschnitt Module): docs.python.org/3/reference/datamodel.htmlAndere Antworten empfehlen, eine Datei in der obersten Ebene des Projekts zu verwenden. Dies ist nicht erforderlich, wenn Sie
pathlib.Path
undparent
(Python 3.4 und höher) verwenden. Betrachten Sie die folgende Verzeichnisstruktur, in der alle Dateien außerREADME.md
undutils.py
weggelassen wurden.In
utils.py
definieren wir die folgende Funktion.In jedem Modul im Projekt können wir jetzt den Projektstamm wie folgt abrufen.
Vorteile : Jedes Modul, das aufruft,
get_project_root
kann verschoben werden, ohne das Programmverhalten zu ändern. Erst wenn das Modulutils.py
verschoben wird, müssen wir es aktualisierenget_project_root
und importieren (Refactoring-Tools können verwendet werden, um dies zu automatisieren).quelle
Alle vorherigen Lösungen scheinen für das, was ich denke, dass Sie brauchen, zu kompliziert zu sein und haben bei mir oft nicht funktioniert. Der folgende einzeilige Befehl macht, was Sie wollen:
quelle
Um den Pfad des "root" -Moduls abzurufen, können Sie Folgendes verwenden:
Interessanter ist jedoch, dass Sie, wenn Sie ein Konfigurations- "Objekt" in Ihrem obersten Modul haben, wie folgt daraus lesen können:
quelle
os
ist standardmäßig nicht verfügbar. Müssen importierenos
. Das Hinzufügen der Zeileimport os
würde die Antwort also vollständiger machen.python3 -m topmodule.submodule.script
geben ./path/to/topmodule/submodule
/path/to/topmodule
Ein Standardweg, um dies zu erreichen, wäre die Verwendung des
pkg_resources
Moduls, das Teil dessetuptools
Pakets ist.setuptools
wird verwendet, um ein installierbares Python-Paket zu erstellen.Sie können
pkg_resources
den Inhalt Ihrer gewünschten Datei als Zeichenfolge zurückgeben undpkg_resources
den tatsächlichen Pfad der gewünschten Datei auf Ihrem System abrufen.Angenommen, Sie haben ein Paket namens
stackoverflow
.Angenommen, Sie möchten von einem Modul aus auf die Datei Rush zugreifen
app.run
. Verwenden Siepkg_resources.resouces_filename
diese Option , um den Pfad zu Rush undpkg_resources.resource_string
den Inhalt von Rush abzurufen. also:Die Ausgabe:
Dies funktioniert für alle Pakete in Ihrem Python-Pfad. Wenn Sie also wissen möchten, wo
lxml.etree
auf Ihrem System vorhanden ist:Ausgabe:
Der Punkt ist, dass Sie diese Standardmethode verwenden können, um auf Dateien zuzugreifen, die auf Ihrem System installiert sind (z. B. pip install xxx oder yum -y install python-xxx) und auf Dateien, die sich in dem Modul befinden, an dem Sie gerade arbeiten.
quelle
Versuchen:
quelle
Unter Code Gibt den Pfad bis zu Ihrem Projektstamm zurück
quelle
Ich hatte auch mit diesem Problem zu kämpfen, bis ich zu dieser Lösung kam. Dies ist meiner Meinung nach die sauberste Lösung.
Fügen Sie in Ihrer setup.py "Pakete" hinzu
In Ihrer python_script.py
quelle
python3 setup.py install
damit zeigte nicht mehr auf den Quellcodeordner, sondern auf das Ei im Inneren~./virtualenv/..../app.egg
. Also musste ich die Konfigurationsdatei in die Paketinstallation aufnehmen.Nur ein Beispiel: Ich möchte runio.py in helper1.py ausführen
Beispiel für einen Projektbaum:
Projektstamm abrufen:
Pfad zum Skript erstellen:
quelle
Dies funktionierte bei Verwendung eines Standard-PyCharm-Projekts mit meiner virtuellen Umgebung (venv) im Projektstammverzeichnis.
Der folgende Code ist nicht der schönste, erhält aber konsistent den Projektstamm. Es gibt den vollständigen Verzeichnispfad von der
VIRTUAL_ENV
Umgebungsvariablen, z/Users/NAME/documents/PROJECT/venv
Anschließend wird der Pfad zuletzt aufgeteilt
/
, wodurch ein Array mit zwei Elementen erstellt wird. Das erste Element ist der Projektpfad, z/Users/NAME/documents/PROJECT
quelle
Ich habe kürzlich versucht, etwas Ähnliches zu tun, und ich habe festgestellt, dass diese Antworten für meine Anwendungsfälle unzureichend sind (eine verteilte Bibliothek, die den Projektstamm erkennen muss). Hauptsächlich habe ich mit verschiedenen Umgebungen und Plattformen gekämpft und immer noch nichts vollkommen Universelles gefunden.
Lokaler Code für das Projekt
Ich habe dieses Beispiel erwähnt und an einigen Stellen verwendet, Django usw.
So einfach das ist, es funktioniert nur, wenn die Datei, in der sich das Snippet befindet, tatsächlich Teil des Projekts ist. Wir rufen nicht das Projektverzeichnis ab, sondern das Verzeichnis des Snippets
In ähnlicher Weise bricht der sys.modules- Ansatz zusammen, wenn er von außerhalb des Einstiegspunkts der Anwendung aufgerufen wird. Insbesondere habe ich festgestellt, dass ein untergeordneter Thread dies nicht ohne Bezug zum Hauptmodul feststellen kann . Ich habe den Import explizit in eine Funktion eingefügt, um einen Import aus einem untergeordneten Thread zu demonstrieren. Wenn Sie ihn auf die oberste Ebene von app.py verschieben, wird dies behoben.
app.py.
settings.py
Das Ausführen dieses Programms führt zu einem Attributfehler:
... daher eine Threading-basierte Lösung
Standortunabhängig
Verwenden Sie dieselbe Anwendungsstruktur wie zuvor, ändern Sie jedoch settings.py
Aufschlüsselung: Zuerst wollen wir die Thread-ID des Haupt-Threads genau finden. In Python3.4 + hat die Threading-Bibliothek
threading.main_thread()
jedoch nicht jeder 3.4+ verwendet, sodass wir alle Threads nach dem Haupt-Thread durchsuchen und dessen ID speichern. Wenn der Hauptthread bereits beendet wurde, wird er nicht in der Liste aufgeführtthreading.enumerate()
. Wir erhebenRuntimeError()
in diesem Fall eine, bis ich eine bessere Lösung finde.Als nächstes finden wir den allerersten Stapelrahmen des Hauptthreads. Mit der cPython-spezifischen Funktion erhalten
sys._current_frames()
wir ein Wörterbuch des aktuellen Stapelrahmens jedes Threads. Mitinspect.getouterframes()
können wir dann den gesamten Stapel für den Haupt-Thread und den allerersten Frame abrufen. current_main_frame = sys._current_frames () [main_id] base_frame = inspect.getouterframes (current_main_frame) [- 1] Schließlich müssen die Unterschiede zwischen Windows- und Linux-Implementierungen voninspect.getouterframes()
behandelt werden. Verwenden Sie den bereinigten Dateinamenos.path.abspath()
undos.path.dirname()
bereinigen Sie die Dinge.Bisher habe ich dies unter Python2.7 und 3.6 unter Windows sowie unter Python3.4 unter WSL getestet
quelle
Wenn Sie mit einem Anaconda-Projekt arbeiten, können Sie PROJECT_ROOT über die Umgebungsvariable -> os.getenv ('PROJECT_ROOT') abfragen. Dies funktioniert nur, wenn das Skript über einen Anaconda-Projektlauf ausgeführt wird.
Wenn Sie nicht möchten, dass Ihr Skript von anaconda-project ausgeführt wird, können Sie den absoluten Pfad der ausführbaren Binärdatei des von Ihnen verwendeten Python-Interpreters abfragen und die Pfadzeichenfolge exklusiv in das Verzeichnis envs extrahieren. Zum Beispiel: Der Python-Interpreter meiner conda env befindet sich unter:
Dies funktioniert nur mit Conda-Projekt mit fester Projektstruktur eines Anaconda-Projekts
quelle
Ich habe die Methode ../ verwendet, um den aktuellen Projektpfad abzurufen.
Beispiel: Projekt1 - D: \ Projekte
src
Konfigurationsdateien
Configuration.cfg
Path = "../ src / ConfigurationFiles / Configuration.cfg"
quelle
Zum Zeitpunkt des Schreibens ist keine der anderen Lösungen sehr eigenständig. Sie hängen entweder von einer Umgebungsvariablen oder von der Position des Moduls in der Paketstruktur ab. Die Top-Antwort mit der 'Django'-Lösung fällt letzterer zum Opfer, indem sie einen relativen Import erfordert. Es hat auch den Nachteil, dass ein Modul auf der obersten Ebene geändert werden muss.
Dies sollte der richtige Ansatz sein, um den Verzeichnispfad des Pakets der obersten Ebene zu finden:
Es funktioniert, indem die erste Komponente in der darin enthaltenen gepunkteten Zeichenfolge
__name__
als Schlüssel verwendet wird, insys.modules
dem das Modulobjekt des Pakets der obersten Ebene zurückgegeben wird. Sein__file__
Attribut enthält den Weg , den wir wollen nach Abschneiden/__init__.py
mitos.path.dirname()
.Diese Lösung ist in sich geschlossen. Es funktioniert überall in jedem Modul des Pakets, einschließlich in der
__init__.py
Datei der obersten Ebene .quelle
Ich musste eine benutzerdefinierte Lösung implementieren, da diese nicht so einfach ist, wie Sie vielleicht denken. Meine Lösung basiert auf Stack Trace Inspection (
inspect.stack()
) +sys.path
und funktioniert einwandfrei, unabhängig von der Position des Python-Moduls, in dem die Funktion aufgerufen wird, oder des Interpreters (ich habe versucht, sie in PyCharm, in einer Poetry-Shell und anderen ... ). Dies ist die vollständige Implementierung mit Kommentaren:quelle
Hier gibt es viele Antworten, aber ich konnte nichts Einfaches finden, das alle Fälle abdeckt. Lassen Sie mich daher auch meine Lösung vorschlagen:
quelle