Quelqu'un peut-il expliquer les importations relatives de python?

174

Je ne peux pas pour la vie de moi faire fonctionner les importations relatives de python. J'ai créé un exemple simple où cela ne fonctionne pas:

La structure du répertoire est:

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

/start.py contient juste: import sub.relative

/sub/relative.py contient juste from .. import parent

Tous les autres fichiers sont vides.

Lors de l'exécution de ce qui suit sur la ligne de commande:

$ cd /
$ python start.py

Je reçois:

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

J'utilise Python 2.6. pourquoi est-ce le cas? Comment faire fonctionner cet exemple de sandbox?

Carl
la source

Réponses:

140

Vous importez à partir du package "sub". start.pyn'est pas lui-même dans un emballage même s'il y a un __init__.pycadeau.

Vous devrez démarrer votre programme à partir d'un répertoire sur parent.py:

./start.py

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

Avec start.py:

import pkg.sub.relative

Maintenant, pkg est le package de niveau supérieur et votre importation relative devrait fonctionner.


Si vous souhaitez vous en tenir à votre mise en page actuelle, vous pouvez simplement utiliser import parent. Parce que vous utilisez start.pypour lancer votre interpréteur, le répertoire où start.pyse trouve se trouve dans votre chemin python. parent.pyy vit comme un module séparé.

Vous pouvez également supprimer en toute sécurité le niveau supérieur __init__.py, si vous n'importez rien dans un script plus haut dans l'arborescence des répertoires.

ebo
la source
2
Vous confondez les termes «module» et «package». 'start.py' représente le module 'start', 'mod' et 'mod.sub' sont des packages, 'mod' est un package de niveau supérieur.
Ferdinand Beyer
34
Merci, mais honnêtement, cela semble vraiment idiot. Pour un si beau langage, je ne peux pas croire que les concepteurs créeraient une telle restriction. N'y a-t-il pas d'autre moyen?
carl
2
Ce n'est pas du tout idiot. Les importations relatives sont un moyen de faire référence à des modules frères dans un package. Si vous souhaitez importer un module de niveau supérieur, utilisez des importations absolues.
Ferdinand Beyer
58
Pas idiot? Donc, dans bash, pas été en mesure d'adresser le répertoire supérieur relatif avec ".." ne vous dérangerait pas?
e-satis
2
Il me semble que l'idée de python est d'utiliser des importations "absolues" depuis le répertoire où vous avez lancé votre script parent. Vous pouvez donc utiliser le chemin absolu "import parent" pour importer le module parent du frère. Et relative importe une sorte d'héritage ou autre.
Odysseus
35

Si vous allez appeler relative.pydirectement et c'est-à-dire si vous voulez vraiment importer depuis un module de niveau supérieur, vous devez l'ajouter explicitement à la sys.pathliste.
Voici comment cela devrait fonctionner:

# 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

Si vous pensez que ce qui précède peut provoquer une sorte d'incohérence, vous pouvez utiliser ceci à la place:

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

sys.path[0] fait référence au chemin à partir duquel le point d'entrée a été exécuté.

AmirHossein
la source
3

Vérification en python3:

python -V
Python 3.6.5

Exemple 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

Si nous l'exécutons comme ceci (juste pour nous assurer que PYTHONPATH est vide):

PYTHONPATH='' python3 start.py

Production:

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

Si nous changeons l'importation dans sub/relative.py

- sub/relative.py
import parent

Si nous l'exécutons comme ceci:

PYTHONPATH='' python3 start.py

Production:

Hello from parent.py

Exemple2:

.
├── 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

Exécutez-le comme:

PYTHONPATH='' python3 sub/start.py

Production:

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

Si nous changeons l'importation dans sub/start.py:

- sub/start.py
import relative
import parent

Exécutez-le comme:

PYTHONPATH='' python3 sub/start.py

Production:

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

Exécutez-le comme:

PYTHONPATH='.' python3 sub/start.py

Production:

Hello from relative.py
Hello from parent.py

Il est également préférable d'utiliser l'importation depuis le dossier racine, c'est-à-dire:

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

Exécutez-le comme:

PYTHONPATH='.' python3 sub/start.py

Production:

Hello from relative.py
Hello from parent.py
mrgloom
la source