Comment faire des importations relatives en Python?
-
09-06-2019 - |
Question
Imaginez cette structure de répertoire:
app/
__init__.py
sub1/
__init__.py
mod1.py
sub2/
__init__.py
mod2.py
Je code mod1
et je dois importer quelque chose à partir de mod2
. Comment devrais-je le faire?
J'ai essayé dans ..s2 import2 mod2
mais je reçois un "Importation relative tentée dans un non-package".
J'ai cherché sur Google mais n'ai trouvé que la "manipulation < sys.path
". hacks. N'y a-t-il pas un moyen propre?
Modifier: tous mes __ init __. py
sont actuellement vides
Edit2: J'essaie de faire cela car sub2 contient des classes qui sont partagées entre plusieurs sous-packages ( sub1
, subX
, etc.).
Edit3: Le comportement recherché est identique à celui décrit dans PEP 366. (merci John B)
La solution
Tout le monde semble vouloir vous dire ce que vous devriez faire plutôt que de simplement répondre à la question.
Le problème est que vous exécutez le module en tant que '__main__' en transmettant le mod1.py en tant qu'argument à l'interpréteur.
De PEP 328 :
Les importations relatives utilisent l'attribut __name__ du module pour déterminer la position de ce module dans la hiérarchie des packages. Si le nom du module ne contient aucune information sur le package (par exemple, il est défini sur '__main__'), les importations relatives sont résolues comme si le module était un module de niveau supérieur, quel que soit l'emplacement du module dans le système de fichiers.
Dans Python 2.6, ils ajoutaient la possibilité de référencer des modules par rapport au module principal. Le PEP 366 décrit ce changement.
Mise à jour : selon Nick Coghlan, l'alternative recommandée consiste à exécuter le module à l'intérieur du package à l'aide du commutateur -m.
Autres conseils
main.py
setup.py
app/ ->
__init__.py
package_a/ ->
__init__.py
module_a.py
package_b/ ->
__init__.py
module_b.py
- Vous exécutez
python main.py
. -
main.py
fait:importer app.package_a.module_a
-
module_a.py
importe-t-il app.package_b.module_b
Alternativement, 2 ou 3 pourraient utiliser: depuis app.package_a import module_a
Cela fonctionnera tant que vous aurez app
dans votre PYTHONPATH. main.py
pourrait être n'importe où alors.
Vous écrivez donc un setup.py
pour copier (installer) l'intégralité du package et des sous-packages de l'application dans les dossiers python du système cible, et main.py
dans le script du système cible dossiers.
Voici la solution qui fonctionne pour moi:
Je fais les importations relatives en tant que à partir de ..sub2 import mod2
et puis, si je veux exécuter mod1.py
, je vais dans le répertoire parent de app
et exécute le module à l'aide du commutateur python -m en tant que python - m app.sup1.mod1
.
La véritable raison de ce problème avec les importations relatives est que les importations relatives fonctionnent en prenant la propriété __ name __
du module. Si le module est exécuté directement, __ nom __
est défini sur __ main __
et ne contient aucune information sur la structure du package. Et c’est la raison pour laquelle python se plaint de l’erreur relative à l’importation dans un non-package
.
Ainsi, en utilisant le commutateur -m, vous fournissez les informations sur la structure du package à python, grâce auxquelles il peut résoudre les importations relatives.
J'ai rencontré ce problème plusieurs fois lors de l'importation relative. Et, après avoir lu toutes les réponses précédentes, je n’étais toujours pas en mesure de trouver une solution propre, sans avoir besoin de mettre du code passe-partout dans tous les fichiers. (Bien que certains commentaires aient été vraiment utiles, merci à @ncoghlan et @XiongChiamiov)
J'espère que cela aidera quelqu'un qui se bat avec le problème des importations relatives, car passer par PEP n'est vraiment pas amusant.
"Guido considère l’exécution de scripts dans un package comme un anti-motif". (rejeté PEP-3122 )
J'ai passé beaucoup de temps à essayer de trouver une solution, à lire des billets sur Stack Overflow et à me dire "il doit y avoir une meilleure façon!". On dirait qu'il n'y en a pas.
Ceci est résolu à 100%:
- app /
- main.py
- paramètres /
- local_setings.py
Importer les paramètres / local_setting.py dans app / main.py:
main.py:
import sys
sys.path.insert(0, "../settings")
try:
from local_settings import *
except ImportError:
print('No Import')
def import_path(fullpath):
"""
Import a file with full path specification. Allows one to
import from anywhere, something __import__ does not do.
"""
path, filename = os.path.split(fullpath)
filename, ext = os.path.splitext(filename)
sys.path.append(path)
module = __import__(filename)
reload(module) # Might be out of date
del sys.path[-1]
return module
J'utilise cet extrait pour importer des modules à partir de chemins d'accès, espérons que cela aidera
explication de la réponse de nosklo
avec des exemples
remarque: tous les fichiers __ init __. py
sont vides.
main.py
app/ ->
__init__.py
package_a/ ->
__init__.py
fun_a.py
package_b/ ->
__init__.py
fun_b.py
app / package_a / fun_a.py
def print_a():
print 'This is a function in dir package_a'
app / package_b / fun_b.py
from app.package_a.fun_a import print_a
def print_b():
print 'This is a function in dir package_b'
print 'going to call a function in dir package_a'
print '-'*30
print_a()
main.py
from app.package_b import fun_b
fun_b.print_b()
si vous exécutez $ python main.py
, il retourne:
This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
- main.py fait:
depuis app.package_b import fun_b
- fun_b.py fait
depuis app.package_a.fun_a importer print_a
so fichier dans le dossier package_b
fichier utilisé dans le dossier package_a
, comme vous le souhaitez. Droit ??
Ceci est malheureusement un hack de sys.path, mais cela fonctionne assez bien.
J'ai rencontré ce problème avec une autre couche: j'avais déjà un module du nom spécifié, mais c'était un mauvais module.
Ce que je voulais faire était le suivant (le module sur lequel je travaillais était le module 3):
mymodule\
__init__.py
mymodule1\
__init__.py
mymodule1_1
mymodule2\
__init__.py
mymodule2_1
import mymodule.mymodule1.mymodule1_1
Notez que j'ai déjà installé mymodule, mais que dans mon installation, je n'ai pas "mymodule1"
.et j'obtiendrais une erreur ImportError car elle tentait d'importer à partir de mes modules installés.
J'ai essayé de faire un sys.path.append, et cela n'a pas fonctionné. Qu'est-ce qui a fonctionné était un sys.path.insert
if __name__ == '__main__':
sys.path.insert(0, '../..')
Donc, c'est un peu comme un bidouillage, mais il faut que tout fonctionne! N'oubliez donc pas que si vous souhaitez que votre décision de remplacer les autres chemins , vous devez utiliser sys.path.insert (0, chemin d'accès) pour que cela fonctionne! Ce fut un point de blocage très frustrant pour moi, beaucoup de gens disent d'utiliser le "ajouter" fonctionne avec sys.path, mais cela ne fonctionne pas si vous avez déjà défini un module (je trouve le comportement très étrange)
Laissez-moi simplement mettre ceci ici pour ma propre référence. Je sais que ce n'est pas un bon code Python, mais j'avais besoin d'un script pour un projet sur lequel je travaillais et je voulais le placer dans un répertoire scripts
.
import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
Comme @EvgeniSergeev le dit dans les commentaires à l'OP, vous pouvez importer du code à partir d'un fichier .py
à un emplacement quelconque avec:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Cette information provient de la réponse SO .
Jetez un coup d'œil à http: // docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports . Tu pourrais faire
from .mod1 import stuff
De doc Python ,
Dans Python 2.5, vous pouvez basculer le comportement de l'import en absolu à l'aide d'une directive
.à partir de __future__ import absolute_import
. Ce comportement d’importation absolue deviendra le comportement par défaut dans une version ultérieure (probablement Python 2.7). Une fois que les importations absolues sont la valeur par défaut,chaîne d'importation
trouvera toujours la version de la bibliothèque standard. Il est suggéré que les utilisateurs commencent à utiliser les importations absolues autant que possible. Il est donc préférable de commencer à écrireà partir de la chaîne d'importation pkg
dans votre code
J'ai trouvé qu'il était plus facile de définir "PYTHONPATH". variable d'environnement dans le dossier supérieur:
bash$ export PYTHONPATH=/PATH/TO/APP
alors:
import sub1.func1
#...more import
bien sûr, PYTHONPATH est "global", mais cela ne m'a pas encore posé de problèmes.
En plus de ce que John B a dit, il semble que définir la variable __ package __
devrait aider, au lieu de changer __ main __
, ce qui pourrait gâcher d'autres choses. Mais autant que j'ai pu tester, cela ne fonctionne pas complètement comme il se doit.
J'ai le même problème et ni le PEP 328 ni le 366 ne résolvent le problème complètement, car tous les deux, à la fin de la journée, il faut que la tête du paquet soit incluse dans sys.path
, autant que j'ai pu comprendre.
Je dois également mentionner que je n’ai pas trouvé comment formater la chaîne qui devrait aller dans ces variables. S'agit-il de "package_head.subfolder.module_name" "
ou quoi?
Supposons que vous couriez au niveau supérieur, puis dans mod1
, utilisez:
import sub2.mod2
au lieu de
from ..sub2 import mod2