web-dev-qa-db-fra.com

Python: importer un module depuis un autre répertoire au même niveau dans la hiérarchie des projets

J'ai vu toutes sortes d'exemples et d'autres questions similaires, mais je n'arrive pas à trouver un exemple qui corresponde exactement à mon scénario. Je me sens un peu fou de demander cela parce qu'il y a tellement de questions similaires, mais je n'arrive pas à faire en sorte que cela fonctionne "correctement". Voici mon projet:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |
        |------- Scripts/
        |           |
        |           |----- __init__.py
        |           |----- CreateUser.py
        |           |----- FindUser.py

Si je déplace "CreateUser.py" dans le répertoire principal user_management, je peux facilement utiliser: "import Modules.LDAPManager" pour importer LDAPManager.py --- cela fonctionne. Ce que je ne peux pas faire (ce que je veux faire), c'est de conserver CreateUser.py dans le sous-dossier Scripts et d'importer LDAPManager.py. J'espérais accomplir cela en utilisant "import user_management.Modules.LDAPManager.py". Ça ne marche pas. En bref, je peux obtenir des fichiers Python plus facilement dans la hiérarchie, mais je ne peux pas obtenir un script Python pour référencer un répertoire vers le haut et vers le bas dans un autre.

Notez que je suis capable de résoudre mon problème en utilisant:

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import Modules.LDAPManager as LDAPManager

J'ai entendu dire que c'était une mauvaise pratique et découragée.

Les fichiers dans les scripts sont destinés à être exécutés directement (le init. Py dans les scripts est-il même nécessaire?). J'ai lu que dans ce cas, je devrais exécuter CreateUser.py avec l'option -m. J'ai essayé quelques variantes et je n'arrive pas à faire en sorte que CreateUser.py reconnaisse LDAPManager.py.

68
CptSupermrkt

Si je déplace CreateUser.py Vers le répertoire principal user_management, je peux facilement utiliser: import Modules.LDAPManager Pour importer LDAPManager.py --- cela fonctionne.

S'il vous plaît, ne le faites pas . De cette façon, le module LDAPManager utilisé par CreateUser sera pas identique à celui importé via autre importations. Cela peut créer des problèmes lorsque vous avez un état global dans le module ou pendant le pickling/unpickling. Avoid Les importations ne fonctionnent que parce que le module se trouve dans le même répertoire.

Lorsque vous avez une structure de paquet, vous devez soit:

  • Utilisez les importations relatives, c'est-à-dire si le CreateUser.py Est dans Scripts/:

     from ..Modules import LDAPManager
    

    Notez que ceci était (notez le passé temps) découragé par PEP 8 = uniquement parce que les anciennes versions de python ne les supportaient pas très bien, mais ce problème a été résolu il y a des années. La actuelle version du PEP 8 ne == les suggère comme une alternative acceptable aux importations absolues. En fait, je aiment les == à l'intérieur des paquets.

  • Utilisez les importations absolues en utilisant le nom complet du package (CreateUser.py Dans Scripts/):

     from user_management.Modules import LDAPManager
    

Pour que le second fonctionne, le paquet user_management Doit être installé à l'intérieur de PYTHONPATH. Pendant le développement, vous pouvez configurer le IDE pour que cela se produise, sans avoir à ajouter manuellement les appels à sys.path.append Où que vous soyez.

Aussi, je trouve étrange que Scripts/ Soit un sous-package. Parce que dans une installation réelle, le module user_management Serait installé sous le site-packages Se trouvant dans le répertoire lib/ (Quel que soit le répertoire utilisé pour installer les bibliothèques dans votre système d'exploitation), tandis que les scripts devrait être installé dans un répertoire bin/ (selon le fichier contenant les exécutables pour votre système d'exploitation).

En fait, je pense que Script/ Ne devrait même pas être sous user_management. Il devrait être au même niveau de user_management. De cette façon, vous not devez utiliser -m, Mais vous devez simplement vous assurer que le paquet peut être trouvé (il s'agit là encore d'une question de configuration de l'EDI , en installant le paquet correctement ou en utilisant PYTHONPATH=. python Scripts/CreateUser.py pour lancer les scripts avec le chemin correct).


En résumé, la hiérarchie [~ # ~] i [~ # ~] == serait:

user_management  (package)
        |
        |------- __init__.py
        |
        |------- Modules/
        |           |
        |           |----- __init__.py
        |           |----- LDAPManager.py
        |           |----- PasswordManager.py
        |

 Scripts/  (*not* a package)
        |  
        |----- CreateUser.py
        |----- FindUser.py

Ensuite, les codes CreateUser.py Et FindUser.py Doivent utiliser des importations absolues pour importer les modules:

from user_management.Modules import LDAPManager

Lors de l'installation, assurez-vous que user_management Se trouve quelque part dans le PYTHONPATH et les scripts du répertoire pour les exécutables afin qu'ils puissent trouver les modules. Pendant le développement, vous utilisez soit la configuration IDE, soit vous lancez CreateUser.py En ajoutant le répertoire parent Scripts/ Au répertoire PYTHONPATH (je veux dire le répertoire qui contient user_management et Scripts):

PYTHONPATH=/the/parent/directory python Scripts/CreateUser.py

Ou vous pouvez modifier le PYTHONPATH globalement pour ne pas avoir à le spécifier à chaque fois. Sur les systèmes d'exploitation Unix (Linux, Mac OS X, etc.), vous pouvez modifier l'un des scripts du shell pour définir la variable externe PYTHONPATH. Sous Windows, vous devez modifier les paramètres des variables d'environnement.


Addendum Je pense que si vous utilisez python2, il vaut mieux éviter les importations relatives implicites en mettant:

from __future__ import absolute_import

au sommet de vos modules. De cette façon, import X toujours == signifie importer le toplevel module X et ne sera jamais essayez d'importer le fichier X.py qui se trouve dans le même répertoire (si ce répertoire ne se trouve pas dans PYTHONPATH). De cette manière, la manière seulement == de faire une importation relative consiste à utiliser la syntaxe explicite (le from . import X) , qui est meilleur ( explicite est meilleur que implicite ).

Cela garantira que vous n'utiliserez jamais les importations relatives implicites "fausses", car elles déclencheraient un ImportError signalant clairement que quelque chose ne va pas. Sinon, vous pourriez utiliser un module qui ne correspond pas à ce que vous pensez.

59
Bakuriu

À partir de Python 2.5, vous pouvez utiliser

from ..Modules import LDAPManager

La période principale vous amène à un niveau supérieur dans votre hiérarchie.

Voir le Python sur références intra-paquet) pour les importations.

14
jonrsharpe

Dans la "racine" __init__.py vous pouvez aussi faire un

import sys
sys.path.insert(1, '.')

ce qui devrait rendre les deux modules importables.

2
rdodev