it-swarm.com.de

über dem obersten Paketfehler beim relativen Import

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?

141
shelper

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.

119
Multihunter
import sys
sys.path.append("..") # Adds higher directory to python modules path.

Versuchen Sie das .. Für mich gearbeitet.

66
jenish Sakhiya

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.

32
User

from package.A import foo

Ich denke es ist klarer als

import sys
sys.path.append("..")
7
Joe Zhow

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())
4
dlamblin

Wenn jemand nach den guten Antworten immer noch ein wenig zu kämpfen hat, sollten Sie Folgendes prüfen:

https://www.daveoncode.com/2017/03/07/wie-nach-solve-python-modulenotfound-no-module-named-import-error/

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.

1
Mierpo

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.

1
Jason DeMorrow

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
0
pelos