Question 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 mod1et j'ai besoin d'importer quelque chose mod2. Comment dois-je le faire?

j'ai essayé from ..sub2 import mod2 mais je reçois une "tentative d'importation relative dans un non-paquet".

Je ai googlé autour mais trouvé seulement "sys.path manipulation "hacks. N'y a-t-il pas une manière propre?


Edit: tous mes __init__.pysont actuellement vides

Edit2: J'essaie de le faire car sub2 contient des classes partagées entre les 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)


429
2017-09-16 14:24


origine


Réponses:


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__ d'un module pour déterminer la position de ce module dans la hiérarchie du package. Si le nom du module ne contient aucune information de 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 sur le système de fichiers.

En Python 2.6, ils ajoutent la possibilité de référencer des modules par rapport au module principal. PEP 366 décrit le changement.

Mettre à jour: Selon Nick Coghlan, l'alternative recommandée est d'exécuter le module à l'intérieur du paquet en utilisant l'interrupteur -m.


262
2017-09-16 14:48



main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. Vous courez python main.py.
  2. main.py Est-ce que: import app.package_a.module_a
  3. module_a.py Est-ce que import app.package_b.module_b

Alternativement 2 ou 3 pourraient utiliser: from app.package_a import module_a

Cela fonctionnera aussi longtemps que vous avez app dans votre PYTHONPATH. main.py pourrait être n'importe où alors.

Donc vous écrivez un setup.py copier (installer) l'ensemble de l'ensemble de l'application et des sous-paquetages dans les dossiers python du système cible, et main.py pour cibler les dossiers de script du système.


104
2018-01-21 12:42



Voici la solution qui fonctionne pour moi:

Je fais les importations relatives en tant que from ..sub2 import mod2 et puis, si je veux courir mod1.py alors je vais dans le répertoire parent de app et exécutez le module en utilisant le commutateur python -m comme python -m app.sub1.mod1.

La vraie raison pour laquelle ce problème se pose avec les importations relatives, c'est que les importations relatives __name__ propriété du module. Si le module est directement exécuté, alors __name__ est réglé sur __main__ et il ne contient aucune information sur la structure du paquet. Et, thats pourquoi python se plaint de la relative import in non-package Erreur.

Ainsi, en utilisant le commutateur -m, vous fournissez les informations sur la structure du paquet à python, à travers lesquelles 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'arrivais toujours pas à trouver comment le résoudre, d'une manière propre, sans avoir besoin de mettre le code standard dans tous les fichiers. (Bien que certains commentaires aient été très utiles, merci à @ncoghlan et @XiongChiamiov)

J'espère que cela aidera quelqu'un qui se bat avec des problèmes d'importations relatives, parce que passer par PEP n'est vraiment pas amusant.


98
2018-03-17 07:43



"Guido considère l'exécution de scripts dans un package comme un anti-pattern" (rejeté PEP-3122)

J'ai passé tellement de temps à essayer de trouver une solution, à lire les articles connexes sur Stack Overflow et à me dire "il doit y avoir un meilleur moyen!". On dirait qu'il n'y en a pas.


40
2017-11-19 16:05



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, espérons que cela aide


24
2017-07-04 23:27



Ceci est résolu à 100%:

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

Importer les paramètres / local_setting.py dans l'application / main.py:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')

21
2018-02-11 12:01



explication de nosklo's répondre avec des exemples

note: tout __init__.py les fichiers 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 tu cours $ 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

donc fichier dans le dossier package_b fichier utilisé dans le dossier package_a, c'est ce que tu veux. Droite??


18
2017-12-08 03:19



C'est malheureusement un hack sys.path, mais ça marche plutôt 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 module3):

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 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 que le travail était un sys.path.insert

if __name__ == '__main__':
    sys.path.insert(0, '../..')

Tellement un hack, mais a tout fait pour fonctionner! Alors gardez à l'esprit, si vous voulez votre décision de remplacer les autres chemins alors vous devez utiliser sys.path.insert (0, pathname) pour le faire fonctionner! Ce fut un point de friction très frustrant pour moi, beaucoup de gens disent d'utiliser la fonction "append" pour sys.path, mais cela ne fonctionne pas si vous avez déjà un module défini (je trouve le comportement très étrange)


11
2018-03-02 22:58