Python: Importieren Sie das Modul aus einem anderen Verzeichnis auf derselben Ebene in der Projekthierarchie

85

Ich habe alle möglichen Beispiele und ähnliche Fragen gesehen, aber ich kann anscheinend kein Beispiel finden, das genau zu meinem Szenario passt. Ich fühle mich wie ein Vollidiot, der das fragt, weil es so viele ähnliche Fragen gibt, aber ich kann einfach nicht scheinen, dass dies "richtig" funktioniert. Hier ist mein Projekt:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |
        |------- Scripts/
        |           |
        |           |----- __init__.py
        |           |----- CreateUser.py
        |           |----- FindUser.py

Wenn ich "CreateUser.py" in das Hauptverzeichnis user_management verschiebe, kann ich einfach "import Modules.LDAPManager"Folgendes verwenden: um LDAPManager.py zu importieren --- das funktioniert. Was ich nicht tun kann (was ich tun möchte), ist CreateUser.py im Unterordner Scripts zu behalten und LDAPManager.py zu importieren. Ich hatte gehofft, dies mit zu erreichen "import user_management.Modules.LDAPManager.py". Das funktioniert nicht. Kurz gesagt, ich kann Python-Dateien dazu bringen, leichter tiefer in die Hierarchie zu schauen, aber ich kann kein Python-Skript bekommen, das auf ein Verzeichnis und auf ein anderes verweist.

Beachten Sie, dass ich mein Problem lösen kann, indem ich:

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import Modules.LDAPManager as LDAPManager

Ich habe gehört, dass dies eine schlechte Praxis ist und entmutigt.

Die Dateien in Skripten sollen direkt ausgeführt werden (ist die init .py in Skripten überhaupt notwendig?). Ich habe gelesen, dass ich in diesem Fall CreateUser.py mit dem Flag -m ausführen sollte. Ich habe einige Variationen davon ausprobiert und kann CreateUser.py einfach nicht dazu bringen, LDAPManager.py zu erkennen.

CptSupermrkt
quelle

Antworten:

66

Wenn ich CreateUser.pyin das Hauptverzeichnis user_management wechsle, kann ich einfach Folgendes verwenden: import Modules.LDAPManagerzum Importieren LDAPManager.py --- das funktioniert.

Bitte nicht . Auf diese Weise das LDAPManagerverwendete Modul CreateUserwird nicht die gleiche wie die über andere Importe importiert werden. Dies kann zu Problemen führen, wenn sich im Modul ein globaler Status befindet oder wenn das Beizen / Entpicken erfolgt. Vermeiden Sie Importe, die nur funktionieren, weil sich das Modul zufällig im selben Verzeichnis befindet.

Wenn Sie eine Paketstruktur haben, sollten Sie entweder:

  • Verwenden Sie relative Importe, dh wenn das in CreateUser.pyist Scripts/:

     from ..Modules import LDAPManager

    Beachten Sie, dass dies von PEP 8 nur deshalb empfohlen wurde (beachten Sie die Vergangenheitsform ), weil alte Versionen von Python sie nicht sehr gut unterstützten, aber dieses Problem wurde vor Jahren gelöst. Die aktuelle Version von PEP 8 schlägt sie als akzeptable Alternative zu absoluten Importen vor. Ich mag sie tatsächlich in Paketen.

  • Verwenden Sie absolute Importe mit dem gesamten Paketnamen ( CreateUser.pyin Scripts/):

     from user_management.Modules import LDAPManager

Damit der zweite funktioniert, user_managementsollte das Paket im installiert werden PYTHONPATH. Während der Entwicklung können Sie die IDE so konfigurieren, dass dies geschieht, ohne dass Sie sys.path.appendirgendwo manuell Anrufe hinzufügen müssen .

Auch finde ich es seltsam, dass Scripts/es sich um ein Unterpaket handelt. Denn in einer realen Installation würde das user_managementModul unter dem site-packagesim lib/Verzeichnis gefundenen Verzeichnis installiert (welches Verzeichnis auch immer zum Installieren von Bibliotheken in Ihrem Betriebssystem verwendet wird), während die Skripte unter einem bin/Verzeichnis installiert werden sollten (welches auch immer ausführbare Dateien für Ihr Betriebssystem enthält).

Tatsächlich glaube ich, dass Script/es nicht einmal unter sein sollte user_management. Es sollte auf dem gleichen Niveau sein user_management. Auf diese Weise müssen Sie nicht verwenden -m, sondern müssen nur sicherstellen, dass das Paket gefunden werden kann (dies ist wiederum eine Frage der Konfiguration der IDE, der korrekten Installation des Pakets oder der Verwendung PYTHONPATH=. python Scripts/CreateUser.pyzum Starten der Skripte mit dem richtigen Pfad).


Zusammenfassend würde ich folgende Hierarchie verwenden:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |

 Scripts/  (*not* a package)
        |  
        |----- CreateUser.py
        |----- FindUser.py

Dann sollte der Code von CreateUser.pyund FindUser.pyabsolute Importe verwenden, um die Module zu importieren:

from user_management.Modules import LDAPManager

Während der Installation stellen Sie sicher, dass user_managementirgendwo in den PYTHONPATHSkripten im Verzeichnis für ausführbare Dateien landet, damit diese die Module finden können. Während der Entwicklung verlassen Sie sich entweder auf die IDE-Konfiguration oder Sie starten das CreateUser.pyHinzufügen des Scripts/übergeordneten Verzeichnisses zum PYTHONPATH(ich meine das Verzeichnis, das beide user_managementund enthält Scripts):

PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py

Oder Sie können das PYTHONPATHglobal ändern, sodass Sie dies nicht jedes Mal angeben müssen. Unter Unix-Betriebssystemen (Linux, Mac OS X usw.) können Sie eines der Shell-Skripte ändern, um die PYTHONPATHexterne Variable zu definieren. Unter Windows müssen Sie die Einstellungen für Umgebungsvariablen ändern.


Nachtrag Ich glaube, wenn Sie Python2 verwenden, ist es besser, implizite relative Importe zu vermeiden, indem Sie Folgendes eingeben:

from __future__ import absolute_import

oben in Ihren Modulen. Auf diese Weise bedeutet import X immer , das Toplevel- Modul zu importieren, Xund es wird niemals versucht, die X.pyDatei zu importieren , die sich im selben Verzeichnis befindet (wenn sich dieses Verzeichnis nicht im Verzeichnis befindet PYTHONPATH). Auf diese Weise besteht die einzige Möglichkeit, einen relativen Import durchzuführen, darin, die explizite Syntax (the from . import X) zu verwenden, die besser ist ( explizit ist besser als implizit ).

Dies stellt sicher, dass Sie niemals die "falschen" impliziten relativen Importe verwenden, da diese ein ImportErrorklares Signal dafür auslösen würden, dass etwas nicht stimmt. Andernfalls könnten Sie ein Modul verwenden, das Ihrer Meinung nach nicht so ist.

Bakuriu
quelle
Wenn Sie relative Importe verwenden, sollten Siepython -m user_management.Scripts.CreateUser
Mononoke
14

Ab Python 2.5 können Sie verwenden

from ..Modules import LDAPManager

Die führende Periode bringt Sie auf eine Stufe in Ihrer Hierarchie.

Informationen zum Import finden Sie in den Python-Dokumenten zu paketinternen Referenzen .

Jonrsharpe
quelle
3

In der "Wurzel" können __init__.pySie auch eine

import sys
sys.path.insert(1, '.')

das sollte beide Module importierbar machen.

rdodev
quelle