842 Stimmen

Wie behebt man "Attempted relative import in non-package" auch mit __init__.py

Ich versuche zu folgen PEP 328 mit der folgenden Verzeichnisstruktur:

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

Unter core_test.py Ich habe die folgende Importanweisung

from ..components.core import GameLoopEvents

Bei der Ausführung erhalte ich jedoch die folgende Fehlermeldung:

tests$ python core_test.py 
Traceback (most recent call last):
  File "core_test.py", line 3, in <module>
    from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package

Bei meiner Suche fand ich " Relativer Pfad funktioniert auch mit __init__.py nicht " und " Importieren eines Moduls aus einem relativen Pfad ", aber sie haben nicht geholfen.

Gibt es etwas, das ich hier übersehe?

2voto

mon Punkte 12567

python <main module>.py funktioniert nicht mit relativem Import

Das Problem ist relativer Import funktioniert nicht, wenn Sie eine __main__ Modul über die Befehlszeile

python <main_module>.py

Es ist eindeutig festgelegt in PEP 338 .

Die Freigabe von 2.5b1 zeigte eine überraschende (wenn auch im Nachhinein offensichtliche) Wechselwirkung zwischen diesem PEP und PEP 328 - explizite relative Importe funktionieren nicht in einem Hauptmodul . Dies ist auf die Tatsache zurückzuführen, dass die relativen Einfuhren von __name__ um die Position des aktuellen Moduls in der Pakethierarchie zu bestimmen. In einem Hauptmodul wird der Wert von __name__ ist immer '__main__' also explizite relative Importe werden immer fehlschlagen (da sie nur für ein Modul innerhalb eines Pakets funktionieren).

Ursache

Das Problem tritt nicht nur bei der Option -m auf. Das Problem ist, dass die relativen Importe auf __name__ und im Hauptmodul, __name__ hat immer den Wert __main__ . Folglich, relative Importe aus dem Hauptmodul einer Anwendung können derzeit nicht richtig funktionieren denn das Hauptmodul weiß nicht, wo es wirklich in den Python-Modul-Namensraum passt (dies ist zumindest theoretisch für die Hauptmodule, die mit dem Schalter -m ausgeführt werden, behebbar, aber direkt ausgeführte Dateien und der interaktive Interpreter haben kein Glück).

Zum weiteren Verständnis siehe Relative Importe in Python 3 für die ausführliche Erklärung und wie man sie überwinden kann.

1voto

SteveCalifornia Punkte 101

Hier ist eine Möglichkeit, die alle verärgern wird, aber ziemlich gut funktioniert. In Tests laufen:

ln -s ../components components

Dann importieren Sie die Komponenten einfach wie gewohnt.

1voto

Kai Aeberli Punkte 1139

Bei mir hat nur dies funktioniert: Ich musste explizit den Wert von Paket in das übergeordnete Verzeichnis, und fügen Sie das übergeordnete Verzeichnis zu sys.path hinzu

from os import path
import sys
if __package__ is None:
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    __package__= "myparent"

from .subdir import something # the . can now be resolved

Ich kann mein Skript jetzt direkt mit python myscript.py .

1voto

Ronny Punkte 166

Ich hatte ähnliche Probleme, und als Software-Ingenieur denke ich, dass einige der hier vorgeschlagenen Lösungen nicht ideal sind. Wenn Sie relative Importe wollen, sollten Sie nicht try/except und dann manchmal einen absoluten Import durchführen. Um ein Programm auszuführen, sollten Sie auch nicht den sys.path ändern müssen.

Außerdem sollte das Programm immer funktionieren, unabhängig von Ihrem aktuellen Arbeitsverzeichnis und unabhängig davon, wie Sie es starten.

Deshalb habe ich eine neue, experimentelle Importbibliothek erstellt: Ultraimport Es ermöglicht dateisystembasierte Importe, unabhängig davon, wie Sie Ihren Code ausführen.

Von der ursprünglichen Frage, würden Sie Ihre core_test.py zu etwas wie ändern

import ultraimport
GameLoopEvents = ultraimport('__dir__/../components/core.py', 'GameLoopEvents')
print(GameLoopEvents)

und es wird immer gefunden, egal wie Sie Ihre Tests durchführen.

$ python -m tests.core_test
<class 'core.GameLoopEvents'>
 python ./tests/core_test.py 
<class 'core.GameLoopEvents'>

Ich habe dieses Beispiel auch in die Beispielordner im Git-Repositorium.

Da die Bibliothek experimentell ist, bin ich an Rückmeldungen interessiert. Es funktioniert für mich, aber es ist nicht weithin getestet, noch.

1voto

Hadi F Punkte 21

Wenn Ihre Projektstruktur wie folgt aussehen würde:

   project
     |
     | --- module1
     |      |
     |      file1.py
     |
     |-----module2
     |     |
     |     file2.py

und Sie werden file1.py aus file2.py importieren, können Sie dies in file2.py tun:

import sys
sys.path.append('.')

import file2

Ich weiß immer noch nicht, warum und wie, aber bei mir hat es funktioniert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X