web-dev-qa-db-fra.com

Importation relative dans Python 3 ne fonctionne pas

J'ai le répertoire suivant:

mydirectory
├── __init__.py
├── file1.py 
└── file2.py

J'ai une fonction f définie dans fichier1.py.

Si, dans file2.py, je fais

from .file1 import f

Je reçois l'erreur suivante:

SystemError: Le module parent '' n'est pas chargé, impossible d'effectuer un .__ relatif importation

Pourquoi? Et comment le faire fonctionner?

49

étant donné que file1 et file2 se trouvent dans le même répertoire, vous n'avez même pas besoin d'avoir un fichier __init__.py. Si vous comptez vous développer, laissez-le là.

Pour importer quelque chose dans un fichier du même répertoire, procédez comme suit

from file1 import f

c'est-à-dire que vous n'avez pas besoin de faire le chemin relatif .file1 car ils sont dans le même répertoire.

Si votre fonction principale, votre script ou tout ce qui va exécuter l'application tout entière se trouve dans un autre répertoire, vous devrez alors définir tout ce qui est relatif à l'endroit où il est exécuté.

24
mrKelley

Lancer des modules à l'intérieur d'un paquet en tant qu'exécutables est une mauvaise pratique.

Lorsque vous développez quelque chose, vous construisez soit une bibliothèque, qui est destinée à être importée par d'autres programmes et il est donc peu logique de permettre l'exécution directe de ses sous-modules, soit vous construisez un exécutable auquel cas il n'y a aucune raison de le faire d'un paquet.

C'est pourquoi dans setup.py vous faites la distinction entre les packages et les scripts. Les packages passeront sous site-packages tandis que les scripts seront installés sous /usr/bin (ou à un emplacement similaire, en fonction du système d'exploitation).

Ma recommandation est donc d'utiliser la disposition suivante:

/
├── mydirectory
|    ├── __init__.py
|    ├── file1.py 
└── file2.py

file2.py importe file1.py comme tout autre code souhaitant utiliser la bibliothèque mydirectory, avec une importation absolute:

from mydirectory.file1 import f

Lorsque vous écrivez un script setup.py pour le projet, il vous suffit de répertorier mydirectory en tant que package et file2.py en tant que script et tout fonctionnera. Pas besoin de tripoter sys.path.

Si, pour une raison quelconque, vous souhaitez réellement exécuter un sous-module d'un package, la méthode appropriée consiste à utiliser le commutateur -m:

python -m mydirectory.file1

Cela charge l'ensemble du package, puis exécute le module en tant que script, permettant ainsi à l'importation relative de réussir.

J'éviterais personnellement de faire cela. Et aussi parce que beaucoup de gens ne savent même pas que vous pouvez le faire et vont finir par avoir la même erreur que vous et pensez que le paquet est cassé.


En ce qui concerne la réponse actuellement acceptée, qui indique que vous devez simplement utiliser une importation relative implicit relative from file1 import f car elle fonctionnera, car elles se trouvent dans le même répertoire:

C'est faux

  • Cela fonctionnera pas en python3, où les importations relatives implicites ne sont pas autorisées et se briseront certainement si vous avez installé un module file1 (puisqu'il sera importé à la place de votre module!).
  • Même si cela fonctionne, le file1 ne sera pas considéré comme faisant partie du paquetage mydirectory. Ceci peut avoir de l'importance. 

    Par exemple, si file1 utilise pickle, le nom du package est important pour un chargement/déchargement correct des données.

40
Bakuriu

Lors du lancement d'un fichier source Python, il est interdit d'importer un autre fichier, qui se trouve dans le package actuel, à l'aide de l'importation relative. 

Dans documentation il est dit:

Notez que les importations relatives sont basées sur le nom du module actuel. Comme le nom du module principal est toujours "__main__", les modules destinés à être utilisés comme module principal d'une application Python doivent toujours utiliser des importations absolues.

Donc, comme @mrKelley a dit, vous devez utiliser une importation absolue dans une telle situation.

19
stalk
myproject/

mypackage
├── __init__.py
├── file1.py
├── file2.py 
└── file3.py

mymainscript.py

Exemple d'importation d'un fichier à un autre

#file1.py
from myproject import file2
from myproject.file3 import MyClass

Importer l'exemple de package dans le maincript

#mymainscript.py
import mypackage

https://docs.python.org/3/tutorial/modules.html#packages

https://docs.python.org/3/reference/import.html#regular-packages

https://docs.python.org/3/reference/simple_stmts.html#the-import-statement

https://docs.python.org/3/glossary.html#term-import-path

La variable sys.path est une liste de chaînes qui détermine le chemin de recherche de l’interpréteur pour les modules. Il est initialisé sur un chemin par défaut issu de la variable d'environnement PYTHONPATH ou d'un paramètre par défaut intégré si PYTHONPATH n'est pas défini. Vous pouvez le modifier en utilisant des opérations de liste standard:

import sys
sys.path.append('/ufs/guido/lib/python')
sys.path.insert(0, '/ufs/guido/myhaxxlib/python')

Son insertion au début a l'avantage de garantir que le chemin est recherché avant les autres (même ceux intégrés) en cas de conflits de noms.

0
The Demz