393 votes

Importations de paquets frères

J'ai essayé de lire les questions sur les importations entre frères et soeurs et même la documentation du paquet mais je n'ai pas encore trouvé de réponse.

Avec la structure suivante :

 LICENSE.md
 README.md
 api
    __init__.py
    api.py
    api_key.py
 examples
    __init__.py
    example_one.py
    example_two.py
 tests
    __init__.py
    test_one.py

Comment les scripts dans les examples y tests importés des répertoires api et être exécuté à partir de la ligne de commande ?

Aussi, j'aimerais éviter l'horrible sys.path.insert hack pour chaque fichier. Sûrement cela peut être fait en Python, non ?

12voto

user1330131 Points 41

Je n'ai pas encore la compréhension de la Pythonologie nécessaire pour voir la façon prévue de partager du code entre des projets non liés sans un hack de sibling/relative import. En attendant ce jour, voici ma solution. Pour examples o tests pour importer des éléments de ..\api ça ressemblerait à ça :

import sys.path
import os.path
# Import from sibling directory ..\api
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..")
import api.api
import api.api_key

6voto

Paolo Rovelli Points 892

Pour les importations de paquets frères et sœurs, vous pouvez utiliser soit l'option insérer ou le ajouter de la méthode [sys.path][2] module :

if __name__ == '__main__' and if __package__ is None:
    import sys
    from os import path
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    import api

Cela fonctionnera si vous lancez vos scripts comme suit :

python examples/example_one.py
python tests/test_one.py

D'autre part, vous pouvez également utiliser l'importation relative :

if __name__ == '__main__' and if __package__ is not None:
    import ..api.api

Dans ce cas, vous devrez lancer votre script avec la commande argument '-m'. (notez que, dans ce cas, vous ne devez pas donner l'option '.py' extension) :

python -m packageName.examples.example_one
python -m packageName.tests.test_one

Bien sûr, vous pouvez mélanger les deux approches, de sorte que votre script fonctionnera quelle que soit la façon dont il est appelé :

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        import api
    else:
        import ..api.api

2voto

AJ. Points 12912

Vous devez regarder comment les déclarations d'importation sont écrites dans le code correspondant. Si examples/example_one.py utilise l'instruction d'importation suivante :

import api.api

...alors il s'attend à ce que le répertoire racine du projet soit dans le chemin du système.

La manière la plus simple de supporter ceci sans aucune modification (comme vous le dites) serait d'exécuter les exemples à partir du répertoire de premier niveau, comme ceci :

PYTHONPATH=$PYTHONPATH:. python examples/example_one.py

2voto

Thunderwood Points 507

TLDR

Cette méthode ne nécessite pas de setuptools, de bidouillages de chemins, d'arguments de ligne de commande supplémentaires ou de spécification du niveau supérieur du paquet dans chaque fichier de votre projet.

Faites simplement un script dans le répertoire parent de ce que vous appelez pour être votre __main__ et tout gérer à partir de là. Pour plus d'explications, continuez à lire.

Explication

Cela peut être réalisé sans avoir à créer un nouveau chemin d'accès, à ajouter des arguments de ligne de commande supplémentaires ou à ajouter du code à chacun de vos programmes pour reconnaître ses frères et sœurs.

La raison de cet échec, comme je crois qu'on l'a déjà mentionné, est que les programmes appelés ont leur __name__ en tant que __main__ . Lorsque cela se produit, le script appelé s'accepte comme étant au niveau supérieur du paquetage et refuse de reconnaître les script dans les répertoires frères.

Cependant, tout ce qui se trouve sous le niveau supérieur du répertoire sera toujours reconnu. TOUT LE MONDE sous le niveau supérieur. Cela signifie que le UNIQUEMENT La chose que vous devez faire pour que les fichiers dans les répertoires frères se reconnaissent/utilisent les uns les autres est de les appeler à partir d'un script dans leur répertoire parent.

Preuve du concept Dans un dir avec la structure suivante :

.
|__Main.py
|
|__Siblings
   |
   |___sib1
   |   |
   |   |__call.py
   |
   |___sib2
       |
       |__callsib.py

Main.py contient le code suivant :

import sib1.call as call

def main():
    call.Call()

if __name__ == '__main__':
    main()

sib1/call.py contient :

import sib2.callsib as callsib

def Call():
    callsib.CallSib()

if __name__ == '__main__':
    Call()

et sib2/callsib.py contient :

def CallSib():
    print("Got Called")

if __name__ == '__main__':
    CallSib()

Si vous reproduisez cet exemple, vous remarquerez que le fait d'appeler Main.py entraînera l'impression de "Got Called", comme défini dans la section sib2/callsib.py même si sib2/callsib.py a été appelé par sib1/call.py . Cependant, si l'on appelle directement sib1/call.py (après avoir apporté les modifications appropriées aux importations), une exception est levée. Même s'il a fonctionné lorsqu'il a été appelé par le script dans son répertoire parent, il ne fonctionnera pas s'il croit se trouver au niveau supérieur du paquetage.

1voto

Au cas où quelqu'un utilisant Pydev sur Eclipse se retrouverait ici : vous pouvez ajouter le chemin du parent du module frère (et donc le parent du module appelant) comme dossier de bibliothèque externe en utilisant Projet->Propriétés et la mise en place Bibliothèques externes sous le menu de gauche Pydev-PYTHONPATH . Ensuite, vous pouvez importer à partir de votre frère ou sœur, par exemple. from sibling import some_class .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X