Kann jemand die relativen Importe von Python erklären?

174

Ich kann nicht für mein ganzes Leben die relativen Importe von Python zum Laufen bringen. Ich habe ein einfaches Beispiel dafür erstellt, wo es nicht funktioniert:

Die Verzeichnisstruktur lautet:

/__init__.py
/start.py
/parent.py
/sub/__init__.py
/sub/relative.py

/start.py enthält nur: import sub.relative

/sub/relative.py enthält nur from .. import parent

Alle anderen Dateien sind leer.

Wenn Sie Folgendes in der Befehlszeile ausführen:

$ cd /
$ python start.py

Ich bekomme:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/home/cvondrick/sandbox/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: Attempted relative import beyond toplevel package

Ich benutze Python 2.6. Warum ist das so? Wie mache ich dieses Sandbox-Beispiel?

Carl
quelle

Antworten:

140

Sie importieren aus dem Paket "sub". start.pyist nicht selbst in einem Paket, auch wenn es ein __init__.pyGeschenk gibt.

Sie müssten Ihr Programm von einem Verzeichnis aus neu starten parent.py:

./start.py

./pkg/__init__.py
./pkg/parent.py
./pkg/sub/__init__.py
./pkg/sub/relative.py

Mit start.py:

import pkg.sub.relative

Jetzt ist pkg das Paket der obersten Ebene und Ihr relativer Import sollte funktionieren.


Wenn Sie bei Ihrem aktuellen Layout bleiben möchten, können Sie einfach verwenden import parent. Da Sie start.pyIhren Interpreter zum Starten verwenden, befindet sich das Verzeichnis start.pyin Ihrem Python-Pfad. parent.pylebt dort als separates Modul.

Sie können die oberste Ebene auch sicher löschen __init__.py, wenn Sie nichts weiter oben im Verzeichnisbaum in ein Skript importieren.

ebo
quelle
2
Sie verwechseln die Begriffe "Modul" und "Paket". 'start.py' repräsentiert das Modul 'start', 'mod' und 'mod.sub' sind Pakete, 'mod' ist ein Toplevel-Paket.
Ferdinand Beyer
34
Danke, aber das scheint ehrlich gesagt wirklich albern. Für eine so schöne Sprache kann ich nicht glauben, dass die Designer eine solche Einschränkung schaffen würden. Gibt es keinen anderen Weg?
Carl
2
Es ist überhaupt nicht albern. Relative Importe sind ein Mittel, um auf Geschwistermodule innerhalb eines Pakets zu verweisen. Wenn Sie ein Toplevel-Modul importieren möchten, verwenden Sie absolute Importe.
Ferdinand Beyer
58
Nicht dumm? Also in Bash, nicht in der Lage gewesen, relative obere Dir mit ".." zu adressieren würde Sie nicht stören?
E-Satis
2
Es scheint mir, dass die Idee des Pythons darin besteht, "absolute" Importe aus dem Verzeichnis zu verwenden, in dem Sie Ihr übergeordnetes Skript gestartet haben. Sie können also den absoluten Pfad "Eltern importieren" verwenden, um das Elternmodul vom Geschwister zu importieren. Und Verwandte importieren irgendeine Art von Vermächtnis oder was auch immer ..
Odysseus
35

Wenn Sie relative.pydirekt anrufen möchten und also wirklich von einem Modul der obersten Ebene importieren möchten, müssen Sie es explizit zur sys.pathListe hinzufügen .
So sollte es funktionieren:

# Add this line to the beginning of relative.py file
import sys
sys.path.append('..')

# Now you can do imports from one directory top cause it is in the sys.path
import parent

# And even like this:
from parent import Parent

Wenn Sie der Meinung sind, dass das oben Gesagte zu Inkonsistenzen führen kann, können Sie stattdessen Folgendes verwenden:

sys.path.append(sys.path[0] + "/..")

sys.path[0] bezieht sich auf den Pfad, von dem aus der Einstiegspunkt ausgeführt wurde.

AmirHossein
quelle
3

Überprüfen Sie es in Python3:

python -V
Python 3.6.5

Beispiel 1:

.
├── parent.py
├── start.py
└── sub
    └── relative.py

- start.py
import sub.relative

- parent.py
print('Hello from parent.py')

- sub/relative.py
from .. import parent

Wenn wir es so ausführen (nur um sicherzustellen, dass PYTHONPATH leer ist):

PYTHONPATH='' python3 start.py

Ausgabe:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/python-import-examples/so-example-v1/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Wenn wir den Import in ändern sub/relative.py

- sub/relative.py
import parent

Wenn wir es so laufen lassen:

PYTHONPATH='' python3 start.py

Ausgabe:

Hello from parent.py

Beispiel 2:

.
├── parent.py
└── sub
    ├── relative.py
    └── start.py

- parent.py
print('Hello from parent.py')

- sub/relative.py
print('Hello from relative.py')

- sub/start.py
import relative
from .. import parent

Führen Sie es wie folgt aus:

PYTHONPATH='' python3 sub/start.py

Ausgabe:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 2, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Wenn wir den Import ändern in sub/start.py:

- sub/start.py
import relative
import parent

Führen Sie es wie folgt aus:

PYTHONPATH='' python3 sub/start.py

Ausgabe:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 3, in <module>
    import parent
ModuleNotFoundError: No module named 'parent'

Führen Sie es wie folgt aus:

PYTHONPATH='.' python3 sub/start.py

Ausgabe:

Hello from relative.py
Hello from parent.py

Es ist auch besser, den Import aus dem Stammordner zu verwenden, dh:

- sub/start.py
import sub.relative
import parent

Führen Sie es wie folgt aus:

PYTHONPATH='.' python3 sub/start.py

Ausgabe:

Hello from relative.py
Hello from parent.py
mrgloom
quelle