Es scheint, dass es hier bereits einige Fragen zum relativen Import in Python 3 gibt, aber nachdem ich viele davon durchgesehen habe, habe ich immer noch keine Antwort auf mein Problem gefunden. Also hier ist die Frage.
Ich habe ein Paket unten gezeigt
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
und ich habe eine einzelne Zeile in test.py:
from ..A import foo
Jetzt bin ich im Ordner von package
und laufe
python -m test_A.test
Ich habe eine Nachricht erhalten
"ValueError: attempted relative import beyond top-level package"
aber wenn ich mich im übergeordneten Ordner von package
z. B. befinde , starte ich:
cd ..
python -m package.test_A.test
alles ist gut.
Jetzt ist meine Frage:
Wenn ich mich im Ordner von package
befinde und das Modul innerhalb des Unterpakets test_A ausführe test_A.test
, ..A
steigt nach meinem Verständnis nur eine Ebene nach oben, die sich noch im package
Ordner befindet, weshalb es eine Meldung gibt beyond top-level package
. Was genau ist der Grund für diese Fehlermeldung?
Antworten:
EDIT: Es gibt bessere / kohärentere Antworten auf diese Frage in anderen Fragen:
Warum funktioniert es nicht? Dies liegt daran, dass Python nicht aufzeichnet, woher ein Paket geladen wurde. Wenn Sie dies tun
python -m test_A.test
, wird im Grunde genommen nur das Wissen verworfen, in demtest_A.test
tatsächlich gespeichert istpackage
(dh espackage
wird nicht als Paket betrachtet). Beim Versuchfrom ..A import foo
wird versucht, auf Informationen zuzugreifen, die nicht mehr vorhanden sind (dh Geschwisterverzeichnisse eines geladenen Speicherorts). Es ist konzeptionell ähnlich wie das Einlassenfrom ..os import path
einer Datei inmath
. Dies wäre schlecht, da die Pakete unterschiedlich sein sollen. Wenn sie etwas aus einem anderen Paket verwenden müssen, sollten sie global mit auf sie verweisenfrom os import path
und Python herausfinden lassen, wo sich das mit$PATH
und befindet$PYTHONPATH
.Wenn Sie verwenden
python -m package.test_A.test
, wird die Verwendung vonfrom ..A import foo
Auflösungen einwandfrei ausgeführt, da der Überblick über die Inhalte bestehtpackage
und Sie nur auf ein untergeordnetes Verzeichnis eines geladenen Speicherorts zugreifen.Warum betrachtet Python das aktuelle Arbeitsverzeichnis nicht als Paket? Keine Ahnung , aber meine Güte , es wäre nützlich.
quelle
-m
Flag zu verwenden und aus dem obigen Verzeichnis auszuführen.sys.path
Hack beinhaltet, sondern die Verwendung von Setuptools , was meiner Meinung nach viel interessanter ist.Versuche dies. Hat für mich gearbeitet.
quelle
A/bar.py
existiert und infoo.py
dirfrom .bar import X
.Annahme:
Wenn Sie sich im
package
Verzeichnis befindenA
undtest_A
separate Pakete sind.Fazit:
..A
Importe sind nur innerhalb eines Pakets erlaubt.Weitere Hinweise: Es
ist hilfreich, die relativen Importe nur innerhalb von Paketen verfügbar zu machen, wenn Sie erzwingen möchten, dass Pakete auf einem beliebigen Pfad platziert werden können
sys.path
.BEARBEITEN:
Das aktuelle Arbeitsverzeichnis befindet sich normalerweise in sys.path. Alle Dateien dort können also importiert werden. Dies ist das Verhalten seit Python 2, als noch keine Pakete vorhanden waren. Wenn Sie das laufende Verzeichnis zu einem Paket machen, können Sie Module als "import .A" und als "import A" importieren. Dies sind dann zwei verschiedene Module. Vielleicht ist dies eine zu berücksichtigende Inkonsistenz.
quelle
python -m package.test_A.test
scheint zu tun, was gewünscht wird, und mein Argument ist, dass dies die Standardeinstellung sein sollte. Können Sie mir ein Beispiel für diese Inkonsistenz geben?#include
wäre sehr nützlich!Keine dieser Lösungen funktionierte in Version 3.6 für mich mit einer Ordnerstruktur wie:
Mein Ziel war es, von Modul1 in Modul2 zu importieren. Was für mich schließlich funktionierte, war seltsamerweise:
Beachten Sie den einzelnen Punkt im Gegensatz zu den bisher erwähnten Zwei-Punkt-Lösungen.
Bearbeiten: Folgendes hat mir geholfen, dies zu klären:
In meinem Fall war das Arbeitsverzeichnis (unerwartet) das Stammverzeichnis des Projekts.
quelle
sys.path.append(".")
funktioniert, weil Sie es im übergeordneten Verzeichnis aufrufen. Beachten Sie, dass es.
immer das Verzeichnis darstellt, in dem Sie den Python-Befehlfrom package.A import foo
Ich denke es ist klarer als
quelle
sys.path.append("..")
. getestet auf Python 3.6Da die populärste Antwort schon sagt, im Grunde sein , weil Ihre
PYTHONPATH
odersys.path
schließt.
aber nicht Ihr Weg zu Ihrem Paket. Der relative Import bezieht sich auf Ihr aktuelles Arbeitsverzeichnis und nicht auf die Datei, in die der Import erfolgt. seltsamerweise.Sie können dies beheben, indem Sie zuerst Ihren relativen Import in absolut ändern und dann entweder mit:
ODER erzwingen Sie den Python-Pfad, wenn Sie auf diese Weise aufgerufen werden, weil:
Mit führen
python -m test_A.test
Sietest_A/test.py
mit__name__ == '__main__'
und aus__file__ == '/absolute/path/to/test_A/test.py'
Das bedeutet, dass
test.py
Sieimport
in der Hauptfallbedingung Ihr absolutes halbgeschütztes verwenden und auch eine einmalige Python-Pfadmanipulation durchführen können:quelle
Bearbeiten: 2020-05-08: Anscheinend wird die von mir zitierte Website nicht mehr von der Person kontrolliert, die den Rat geschrieben hat, daher entferne ich den Link zur Website. Danke, dass du mich über baxx informiert hast.
Wenn jemand nach den großartigen Antworten immer noch Probleme hat, habe ich auf einer Website Ratschläge gefunden, die nicht mehr verfügbar sind.
Wesentliches Zitat von der Website, die ich erwähnt habe:
Es ist ziemlich offensichtlich, dass es so sein muss, wenn man nachträglich darüber nachdenkt. Ich habe versucht, sys.path.append ('..') in meinen Tests zu verwenden, bin jedoch auf das von OP gepostete Problem gestoßen. Durch Hinzufügen der Definition von import und sys.path vor meinen anderen Importen konnte ich das Problem lösen.
quelle
Wenn Sie eine
__init__.py
in einem oberen Ordner haben, können Sie den Import wieimport file/path as alias
in dieser Init-Datei initialisieren . Dann können Sie es für niedrigere Skripte verwenden als:quelle
Meiner bescheidenen Meinung nach verstehe ich diese Frage folgendermaßen:
[FALL 1] Wenn Sie einen absoluten Import wie starten
oder
oder
Sie setzen den Import-Anker tatsächlich so
test_A
, dass er ein Paket der obersten Ebene isttest_A
. Wenn wir also test.py do habenfrom ..A import xxx
, entkommen Sie dem Anker, und Python lässt dies nicht zu.[FALL 2] Wenn Sie dies tun
oder
Ihr Anker wird
package
, so zupackage/test_A/test.py
tun ,from ..A import xxx
den Anker (noch im Innern nicht entkommenpackage
Ordner) und Python glücklich akzeptiert dies.Zusamenfassend:
Darüber hinaus können wir verwenden Voll qualifizierte Modulnamen (FQMN) Um dieses Problem zu untersuchen.
Überprüfen Sie jeweils FQMN:
test.__name__
=package.test_A.test
test.__name__
=test_A.test
Für CASE2 führt a zu
from .. import xxx
einem neuen Modul mit FQMN =package.xxx
, was akzeptabel ist.Während für CASE1 der
..
von innenfrom .. import xxx
aus dem Startknoten (Anker) von springt,test_A
ist dies von Python NICHT zulässig.quelle
Nicht sicher in Python 2.x, aber in Python 3.6, vorausgesetzt, Sie versuchen, die gesamte Suite auszuführen, müssen Sie nur verwenden
-t
Also auf einer Struktur wie
Man könnte zum Beispiel verwenden:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
Und immer noch die
my_module.my_class
ohne große Dramen importieren .quelle