Es scheint, dass es hier schon einige Fragen zum relativen Import in Python 3 gibt, aber nachdem ich viele davon durchgegangen war, fand ich immer noch keine Antwort auf meine Frage. 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 einzige Zeile in test.py:
from ..A import foo
jetzt bin ich im Ordner package
und laufe
python -m test_A.test
Ich habe eine Nachricht erhalten
"ValueError: attempted relative import beyond top-level package"
wenn ich mich jedoch im übergeordneten Ordner von package
befinde, führe ich beispielsweise Folgendes aus:
cd ..
python -m package.test_A.test
alles ist gut.
Nun ist meine Frage: Wenn ich mich im Ordner von package
befinde und das Modul innerhalb des test_A-Unterpakets als test_A.test
ausführt, geht ..A
meines Wissens nach nur eine Ebene hoch, was immer noch der Fall ist innerhalb des package
-Ordners gibt es eine Nachricht, in der beyond top-level package
angezeigt wird. Was ist genau der Grund für diese Fehlermeldung?
Das gleiche Problem wird in dieser Frage mit einer kohärenteren Antwort festgestellt: Import von Geschwisterpaketen
Warum funktioniert das nicht? Dies liegt daran, dass Python nicht aufzeichnet, woher ein Paket geladen wurde. Wenn Sie also python -m test_A.test
ausführen, wird die Kenntnis, dass test_A.test
tatsächlich in package
gespeichert ist, im Wesentlichen verworfen (d. H. package
wird nicht als Paket betrachtet). Versuch from ..A import foo
versucht, auf Informationen zuzugreifen, die nicht mehr vorhanden sind (d. H. Gleichgeordnete Verzeichnisse eines geladenen Speicherorts). Es ist konzeptionell ähnlich dem Zulassen von from ..os import path
in einer Datei in math
. Dies wäre schlecht, weil die Pakete unterschiedlich sein sollen. Wenn sie etwas aus einem anderen Paket verwenden müssen, sollten sie global mit from os import path
darauf verweisen und Python ermitteln lassen, wo sich das mit $PATH
und $PYTHONPATH
befindet.
Wenn Sie python -m package.test_A.test
verwenden, wird die Verwendung von from ..A import foo
einfach aufgelöst, da es den Inhalt von package
aufzeichnet und Sie nur auf ein untergeordnetes Verzeichnis eines geladenen Speicherorts zugreifen.
Warum betrachtet Python das aktuelle Arbeitsverzeichnis nicht als Paket?NO CLUE, aber es wäre nützlich.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
Versuchen Sie das .. Für mich gearbeitet.
Annahme:
Wenn Sie sich im Verzeichnis package
befinden, sind A
und test_A
separate Pakete.
Fazit:..A
-Importe sind nur innerhalb eines Pakets zulässig.
Weitere Hinweise:
Das relative Importieren nur innerhalb von Paketen verfügbar zu machen, ist nützlich, wenn Sie erzwingen möchten, dass Pakete in einem beliebigen Pfad unter sys.path
platziert werden können.
BEARBEITEN:
Bin ich der einzige, der das für verrückt hält !? Warum wird das aktuelle Arbeitsverzeichnis in der Welt nicht als Paket betrachtet? - Multihunter
Das aktuelle Arbeitsverzeichnis befindet sich normalerweise in sys.path. Daher sind alle Dateien dort importierbar. Dies ist ein Verhalten seit Python 2, als Pakete noch nicht vorhanden waren. Wenn Sie das laufende Verzeichnis zu einem Paket machen, können Sie Module als "import .A" und als "import A" importieren, was dann zwei verschiedene Module wäre. Vielleicht ist dies eine Inkonsistenz, die zu berücksichtigen ist.
from package.A import foo
Ich denke es ist klarer als
import sys
sys.path.append("..")
Wie die gängigste Antwort vermuten lässt, liegt dies im Grunde daran, dass Ihre PYTHONPATH
oder sys.path
.
enthält, jedoch nicht Ihren Pfad 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 dieses Problem beheben, indem Sie zuerst den relativen Import in "Absolut" ändern und dann entweder mit folgendem Befehl beginnen:
PYTHONPATH=/path/to/package python -m test_A.test
ODER den Python-Pfad erzwingen, wenn er auf diese Weise aufgerufen wird, weil:
Mit python -m test_A.test
führen Sie test_A/test.py
mit __== '__main__'
und __file__ == '/absolute/path/to/test_A/test.py'
aus
Das bedeutet, dass Sie in test.py
Ihre absolute import
verwenden können, die in der Hauptfallbedingung semi-protected ist, und auch eine einmalige Python-Pfadmanipulation durchführen können:
from os import path
…
def main():
…
if __== '__main__':
import sys
sys.path.append(path.join(path.dirname(__file__), '..'))
from A import foo
exit(main())
Wenn jemand nach den guten Antworten immer noch ein wenig zu kämpfen hat, sollten Sie Folgendes prüfen:
Wesentliches Zitat von der obigen Seite:
"Dasselbe kann programmgesteuert auf diese Weise angegeben werden:
sys importieren
sys.path.append ('..')
Natürlich muss der obige Code vor dem anderen Import .__ geschrieben werden. Aussage.
Es ist ziemlich offensichtlich, dass es so sein muss, wenn man darüber nachdenkt. Ich habe versucht, die sys.path.append ('..') in meinen Tests zu verwenden, stieß jedoch auf das von OP veröffentlichte Problem. Durch das Hinzufügen der Definition import und sys.path vor meinen anderen Importen konnte ich das Problem lösen.
Keine dieser Lösungen funktionierte für mich in 3.6 mit einer Ordnerstruktur wie:
package1/
subpackage1/
module1.py
package2/
subpackage2/
module2.py
Mein Ziel war es, von Modul1 in Modul2 zu importieren. Was für mich schließlich funktioniert hat, war seltsamerweise:
import sys
sys.path.append(".")
Beachten Sie den einzelnen Punkt im Gegensatz zu den bisher genannten Zweipunktlösungen.
Edit: Folgendes hat mir dabei geholfen zu klären:
import os
print (os.getcwd())
In meinem Fall war das Arbeitsverzeichnis (unerwartet) der Stamm des Projekts.
wenn Sie einen __init__.py
in einem übergeordneten Ordner haben, können Sie den Import als import file/path as alias
in dieser Init-Datei initialisieren. Dann können Sie es für niedrigere Skripte verwenden als:
import alias