it-swarm-fr.com

Comment faire des importations relatives en Python?

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)

498
Joril

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.

320
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Vous exécutez python main.py.
  2. main.py fait: import app.package_a.module_a
  3. 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.

121
nosklo

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.

118
Pankaj

"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.

48
lesnik

Ceci est résolu à 100%:

  • app /
    • main.py
  • paramètres/
    • local_setings.py

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')
31
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

24
iElectric

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

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: from app.package_b import fun_b
  • fun_b.py fait 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??

21
suhailvs

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)

12
Garrett Berg

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__), "..")))
10
milkypostman

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 .

8
LondonRob

Jetez un oeil à http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports . Vous pourriez faire

from .mod1 import stuff
4
mossplix

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 à écrire from pkg import string dans votre code.

2
jung rhew

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?

1
Gabriel

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.

1
Andrew_1510