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 de mod2
. Comment devrais-je le faire?
J'ai essayé from ..sub2 import mod2
mais je reçois une "Tentative d'importation relative dans un non-package".
J'ai cherché sur Google mais je n'ai trouvé que "sys.path
manipulation". N'y a-t-il pas un moyen propre?
Edit: tous mes __init__.py
sont actuellement vides
Edit2: J'essaie de faire cela parce que sub2 contient des classes qui sont partagées par plusieurs sous-packages (sub1
, subX
, etc.).
Edit3: Le comportement que je recherche est le même que celui décrit dans PEP 366 (merci John B)
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 __ d'un 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 ajoutent la possibilité de référencer des modules par rapport au module principal. PEP 366 décrit le 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.
main.py
setup.py
app/ ->
__init__.py
package_a/ ->
__init__.py
module_a.py
package_b/ ->
__init__.py
module_b.py
python main.py
.main.py
fait: import app.package_a.module_a
module_a.py
fait import app.package_b.module_b
Vous pouvez également utiliser 2 ou 3: from 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 les dossiers de script du système cible.
Voici la solution qui fonctionne pour moi:
Je fais les importations relatives en tant que from ..sub2 import mod2
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 python -m change comme python -m app.sub1.mod1
.
La vraie raison pour laquelle ce problème se produit 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, alors __name__
est défini sur __main__
et il ne contient aucune information sur la structure du package. Et c’est pourquoi python se plaint de l’erreur relative import in non-package
.
Ainsi, en utilisant le commutateur -m, vous fournissez les informations sur la structure du paquet à Python, grâce auxquelles il peut résoudre les importations relatives avec succès.
J'ai rencontré ce problème plusieurs fois en faisant des importations relatives. 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 à 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 un problème d'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-modèle" (rejeté PEP-3122 )
J'ai passé tellement de temps à essayer de trouver une solution, à lire des articles sur Stack Overflow et à me dire "il doit y avoir une meilleure solution!". On dirait qu'il n'y en a pas.
Ceci est résolu à 100%:
Importez 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, j'espère que cela aidera
explication de nosklo's
réponse avec des exemples
note: 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
def print_a():
print 'This is a function in dir package_a'
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()
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
from app.package_b import fun_b
from app.package_a.fun_a import print_a
so fichier dans le dossier package_b
a utilisé le fichier dans le dossier package_a
, ce que vous voulez. Droite??
Ceci est malheureusement un sys.path bidouille, 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 le 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 un ImportError parce qu'il essayait d'importer de mes modules installés.
J'ai essayé de faire un sys.path.append, et cela n'a pas fonctionné. Qu'est-ce qui fonctionnait était sys.path.insert
if __== '__main__':
sys.path.insert(0, '../..')
Tellement gentil, mais j'ai tout fait pour que ça marche! Alors gardez à l'esprit que si vous voulez que votre décision remplacez les autres chemins, vous devez utiliser sys.path.insert (0, chemin d'accès) pour que cela fonctionne! C’était un point de blocage très frustrant pour moi, beaucoup de gens disent d’utiliser la fonction "append" de 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 juste mettre ceci ici pour ma propre référence. Je sais que le code Python n'est pas bon, 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
situé à un emplacement quelconque avec:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Ceci est pris de this SO answer .
Jetez un oeil à http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports . Vous pourriez faire
from .mod1 import stuff
De doc Python ,
Dans Python 2.5, vous pouvez basculer le comportement de l'importation en importations absolues à l'aide d'une directive
from __future__ import absolute_import
. Ce comportement d’importation absolue deviendra le comportement par défaut dans une future version (probablement Python 2.7). Une fois que les importations absolues sont la valeur par défaut,import string
trouvera toujours la version de la bibliothèque standard. Il est suggéré que les utilisateurs commencent à utiliser autant que possible les importations absolues. Il est donc préférable de commencer à écrirefrom pkg import string
dans votre code.
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, d'ici la fin de la journée, ont besoin que la tête du paquet soit incluse dans sys.path
, autant que je puisse comprendre.
Je devrais également mentionner que je n'ai pas trouvé comment formater la chaîne qui devrait aller dans ces variables. Est-ce "package_head.subfolder.module_name"
ou quoi?
J'ai trouvé qu'il était plus facile de définir la variable d'environnement "PYTHONPATH" dans le dossier principal:
bash$ export PYTHONPATH=/PATH/TO/APP
ensuite:
import sub1.func1
#...more import
bien sûr, PYTHONPATH est "global", mais cela ne m'a pas encore posé de problèmes.