web-dev-qa-db-fra.com

Importation de modules à partir du dossier parent

Je suis en cours d'exécution Python 2.5.

Ceci est mon arborescence de dossiers:

ptdraft/
  nib.py
  simulations/
    life/
      life.py

(J'ai aussi __init__.py dans chaque dossier, omis ici pour des raisons de lisibilité)

Comment importer le module nibdepuis l'intérieur du module lifename__? J'espère qu'il est possible de se passer de bricoler avec sys.path.

Remarque: Le module principal en cours d'exécution se trouve dans le dossier ptdraftname__.

491
Ram Rachum

Il semble que le problème ne soit pas lié au fait que le module se trouve dans un répertoire parent ou quoi que ce soit du genre.

Vous devez ajouter le répertoire qui contient ptdraft à PYTHONPATH.

Vous avez dit que import nib fonctionnait avec vous, cela signifie probablement que vous avez ajouté ptdraft (pas son parent) à PYTHONPATH.

101
hasen

Vous pouvez utiliser des importations relatives (python> = 2.5):

from ... import nib

(Nouveautés de Python 2.5) PEP 328: Importations absolues et relatives

EDIT: ajout d'un autre point '.' monter deux paquets

539
f3lix

Les importations relatives (comme dans from .. import mymodule) ne fonctionnent que dans un package. Pour importer 'mymodule' qui se trouve dans le répertoire parent de votre module actuel:

import os,sys,inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir) 

import mymodule

edit: l'attribut __file__ n'est pas toujours indiqué. Au lieu d'utiliser os.path.abspath(__file__), j'ai maintenant suggéré d'utiliser le module inspect pour récupérer le nom de fichier (et le chemin) du fichier actuel.

229
Remi

J'ai posté une réponse similaire également à la question concernant les importations de paquets frères. Vous pouvez le voir ici . Ce qui suit est testé avec Python 3.6.5, (Anaconda, version 4.5.1), ordinateur Windows 10.

Solution sans sys.path hacks

Installer

J'assume la même structure de dossier que dans la question

.
└── ptdraft
    ├── __init__.py
    ├── nib.py
    └── simulations
        ├── __init__.py
        └── life
            ├── __init__.py
            └── life.py

J'appelle le . le dossier racine et, dans mon cas, il se trouve dans C:\tmp\test_imports.

Pas

1) Ajoutez un fichier setup.py au dossier racine

Le contenu du setup.py peut être simplement

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

Fondamentalement, "tout" setup.py fonctionnerait. Ceci est juste un exemple de travail minimal.

2) Utiliser un environnement virtuel

Si vous connaissez bien les environnements virtuels, activez-en un et passez à l'étape suivante. L'utilisation des environnements virtuels n'est pas absolument nécessaire, mais ils vont vraiment vous aider à long terme (lorsque vous avez plus d'un projet en cours..). Les étapes les plus élémentaires sont (exécutées dans le dossier racine)

  • Créer env virtuel
    • python -m venv venv
  • Activer env virtuel
    • . /venv/bin/activate (Linux) ou ./venv/Scripts/activate (Win)

Pour en savoir plus à ce sujet, il suffit de sortir Google du "didacticiel python virtual env" ou similaire. Vous n'avez probablement jamais besoin d'autres commandes que la création, l'activation et la désactivation.

Une fois que vous avez créé et activé un environnement virtuel, votre console doit donner le nom de l’environnement virtuel entre parenthèses.

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

3) pip installer votre projet en état éditable

Installez votre paquet de niveau supérieur myprojecten utilisant pipname__. L'astuce consiste à utiliser l'indicateur -e lors de l'installation. De cette façon, il est installé dans un état éditable et toutes les modifications apportées aux fichiers .py seront automatiquement incluses dans le package installé.

Dans le répertoire racine, exécutez

pip install -e . (notez le point, il correspond à "répertoire actuel")

Vous pouvez également voir qu'il est installé à l'aide de pip freeze

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

4) Importer en ajoutant mainfolderà chaque importation

Dans cet exemple, mainfolderserait ptdraftname__. Cela a pour avantage que vous ne rencontrerez pas de conflits de noms avec d'autres noms de modules (provenant de la bibliothèque standard Python ou de modules tiers).


Exemple d'utilisation

nib.py

def function_from_nib():
    print('I am the return value from function_from_nib!')

vie.py

from ptdraft.nib import function_from_nib

if __== '__main__':
    function_from_nib()

Courir life.py

(venv) PS C:\tmp\test_imports> python .\ptdraft\simulations\life\life.py
I am the return value from function_from_nib!
63
np8

Si l'ajout de votre dossier de modules à PYTHONPATH n'a pas fonctionné, vous pouvez modifier la liste sys.path dans laquelle votre interpréteur Python recherche les modules. pour importer, le documentation python dit:

Lorsqu'un module nommé spam est importé, l'interpréteur recherche d'abord un module intégré portant ce nom. S'il ne le trouve pas, il recherche ensuite un fichier nommé spam.py dans une liste de répertoires fournie par la variable sys.path. sys.path est initialisé à partir de ces emplacements:

  • le répertoire contenant le script d'entrée (ou le répertoire actuel).
  • PYTHONPATH (liste de noms de répertoires, avec la même syntaxe que la variable Shell PATH).
  • la valeur par défaut dépendante de l'installation.

Après l’initialisation, les programmes Python peuvent modifier sys.path . Le répertoire contenant le script en cours d'exécution est placé au début du chemin de recherche, devant le chemin de la bibliothèque standard. Cela signifie que les scripts de ce répertoire seront chargés à la place des modules du même nom dans le répertoire de la bibliothèque. Ceci est une erreur sauf si le remplacement est prévu.

Sachant cela, vous pouvez effectuer les opérations suivantes dans votre programme:

import sys
# Add the ptdraft folder path to the sys.path list
sys.path.append('/path/to/ptdraft/')

# Now you can import your module
from ptdraft import nib
# Or just
import ptdraft
50
Ricardo Murillo

Vous pouvez utiliser le système d'exploitation dépendant du chemin d'accès dans "chemin de recherche de module" qui est répertorié dans sys.path. Ainsi, vous pouvez facilement ajouter un répertoire parent comme suit

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

Si vous souhaitez ajouter un répertoire parent-parent,

sys.path.insert(0,'../..')
40
Shinbero

Je ne sais pas grand chose à propos de Python 2.
En python 3, le dossier parent peut être ajouté comme suit:

import sys 
sys.path.append('..')

... et puis on peut importer des modules à partir de celui-ci

34
Rob Ellenbroek

Voici une réponse simple afin que vous puissiez voir comment cela fonctionne, petit et multiplate-forme.
Il utilise uniquement les modules intégrés (os, sys et inspect), donc cela devrait fonctionner
sur n’importe quel système d’exploitation (OS), car Python est conçu pour cela.

Code plus court pour la réponse - moins de lignes et de variables

from inspect import getsourcefile
import os.path as path, sys
current_dir = path.dirname(path.abspath(getsourcefile(lambda:0)))
sys.path.insert(0, current_dir[:current_dir.rfind(path.sep)])
import my_module  # Replace "my_module" here with the module name.
sys.path.pop(0)

Pour un nombre de lignes inférieur à celui-ci, remplacez la deuxième ligne par import os.path as path, sys, inspect,
ajoute inspect. au début de getsourcefile (ligne 3) et supprime la première ligne.
- Cependant, tout le module est importé et peut nécessiter plus de temps, de mémoire et de ressources.

Le code de ma réponse (version longue)

from inspect import getsourcefile
import os.path
import sys

current_path = os.path.abspath(getsourcefile(lambda:0))
current_dir = os.path.dirname(current_path)
parent_dir = current_dir[:current_dir.rfind(os.path.sep)]

sys.path.insert(0, parent_dir)

import my_module  # Replace "my_module" here with the module name.

Il utilise un exemple tiré d’une réponse Stool Overflow Comment puis-je obtenir le chemin du courant?
fichier exécuté en Python?
== pour trouver la source (nom de fichier) du code en cours d'exécution avec un outil intégré.

from inspect import getsourcefile  
from os.path import abspath  

Ensuite, partout où vous voulez trouver le fichier source, utilisez simplement:

abspath(getsourcefile(lambda:0))

Mon code ajoute un chemin de fichier à sys.path, le liste de chemins python
car cela permet à Python d'importer des modules à partir de ce dossier.

Après avoir importé un module dans le code, il est conseillé d'exécuter sys.path.pop(0) sur une nouvelle ligne.
lorsque ce dossier ajouté a un module portant le même nom qu'un autre module importé
plus tard dans le programme. Vous devez supprimer l'élément de liste ajouté avant l'importation, pas les autres chemins.
Si votre programme n’importe pas d’autres modules, il est prudent de ne pas supprimer le chemin du fichier car
une fois le programme terminé (ou le redémarrage de Python Shell), toutes les modifications apportées à sys.path disparaissent.

Notes sur une variable de nom de fichier

Ma réponse n'utilise pas la variable __file__ pour obtenir le chemin du fichier/nom du fichier en cours d'exécution
code parce que les utilisateurs l’ont souvent décrit comme étant non fiable . Tu ne devrais pas l'utiliser
pour importer des modules du dossier parent ​​dans des programmes utilisés par d'autres personnes.

Quelques exemples où cela ne fonctionne pas (citation de this Stack Overflow question):

• il ne peut pas se trouve sur certaines plates-formes • il arrive parfois ce n'est pas le chemin du fichier complet

  • py2exe n'a pas d'attribut __file__, mais il existe une solution de contournement
  • Lorsque vous utilisez IDLE avec execute(), il n'y a pas d'attribut __file__
  • OS X 10.6 où je reçois NameError: global name '__file__' is not defined
29
Edward

Voici une solution plus générique qui inclut le répertoire parent dans sys.path (fonctionne pour moi):

import os.path, sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
27
Mike

Pour moi, le plus court et le meilleur choix pour accéder au répertoire parent est:

sys.path.append(os.path.dirname(os.getcwd()))

ou:

sys.path.insert(1, os.path.dirname(os.getcwd()))

os.getcwd () renvoie le nom du répertoire de travail en cours, os.path.dirname (nom_répertoire) renvoie le nom du répertoire indiqué.

En fait, à mon avis, l’architecture de projet Python devrait être réalisée de la manière suivante: aucun module du répertoire enfant n’utilisera un module du répertoire parent. Si quelque chose comme cela se produit, il convient de repenser l'arborescence du projet.

Une autre méthode consiste à ajouter un répertoire parent à la variable d’environnement système PYTHONPATH.

10
zviad

import sys sys.path.append('../')

7
Andong Zhan

Lorsque vous n'êtes pas dans un environnement de package avec des fichiers __init__.py, la bibliothèque pathlib (incluse avec> = Python 3.4) rend très concis et intuitif l'ajout du chemin du répertoire parent au répertoire PYTHONPATH:

import sys
from pathlib import Path
sys.path.append(str(Path('.').absolute().parent))
6
itmatters

Les solutions mentionnées ci-dessus conviennent également. Une autre solution à ce problème est

Si vous souhaitez importer quelque chose du répertoire de niveau supérieur. Ensuite,

from ...module_name import *

De même, si vous souhaitez importer un module à partir du répertoire parent. Ensuite,

from ..module_name import *

De même, si vous souhaitez importer un module à partir du répertoire parent. Ensuite,

from ...module_name.another_module import *

De cette façon, vous pouvez importer n'importe quelle méthode si vous le souhaitez.

6
Ravin Gupta

même style que la réponse précédente - mais en moins de lignes: P

import os,sys
parentdir = os.path.dirname(__file__)
sys.path.insert(0,parentdir)

fichier renvoie l'emplacement où vous travaillez

4
YFP

J'ai trouvé que la manière suivante fonctionne pour importer un package à partir du répertoire parent du script. Dans l'exemple, je voudrais importer des fonctions dans le package env.py à partir du package app.db.

.
└── my_application
    └── alembic
        └── env.py
    └── app
        ├── __init__.py
        └── db
import os
import sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
2
Yu N.

Travailler avec les bibliothèques. Créez une bibliothèque appelée nib, installez-la à l’aide de setup.py, laissez-la résider dans des packages site et vos problèmes seront résolus. Vous n'avez pas à ranger tout ce que vous faites dans un seul paquet. Le casser en morceaux.

1
user3181121

Sur un système Linux, vous pouvez créer un lien symbolique du dossier "life" vers le fichier nib.py. Ensuite, vous pouvez simplement l'importer comme ceci:

import nib
0
Erel Segal-Halevi