Comment importer un module Python étant donné son chemin relatif?
Par exemple, si dirFoo
contient Foo.py
et dirBar
, et dirBar
contient Bar.py
, comment puis-je importer Bar.py
dans Foo.py
?
Voici une représentation visuelle:
dirFoo\
Foo.py
dirBar\
Bar.py
Foo
souhaite inclure Bar
, mais la restructuration de la hiérarchie des dossiers n'est pas une option.
En supposant que vos deux répertoires sont de véritables packages Python (contiennent le fichier __init__.py
], voici une solution sûre pour l'inclusion de modules par rapport à l'emplacement du script.
Je suppose que vous voulez faire cela, car vous devez inclure un ensemble de modules avec votre script. J'utilise ceci en production dans plusieurs produits et fonctionne dans de nombreux scénarios spéciaux tels que: les scripts appelés depuis un autre répertoire ou exécutés avec python execute au lieu d'ouvrir un nouvel interpréteur.
import os, sys, inspect
# realpath() will make your script run, even if you symlink it :)
cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
if cmd_folder not in sys.path:
sys.path.insert(0, cmd_folder)
# Use this if you want to include modules from a subfolder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
# Info:
# cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
# __file__ fails if the script is called in different ways on Windows.
# __file__ fails if someone does os.chdir() before.
# sys.argv[0] also fails, because it doesn't not always contains the path.
En prime, cette approche vous permet de forcer Python à utiliser votre module à la place de ceux installés sur le système.
Attention! Je ne sais pas vraiment ce qui se passe lorsque le module actuel est dans un fichier Egg
. Cela échoue probablement aussi.
Assurez-vous que dirBar a le fichier __init__.py
- cela transforme un répertoire dans un package Python.
Vous pouvez également ajouter le sous-répertoire à votre chemin Python afin qu'il soit importé en tant que script normal.
import sys
sys.path.insert(0, <path to dirFoo>)
import Bar
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)
import mymodule
Il suffit de faire des choses simples pour importer le fichier .py à partir d’un dossier différent.
Disons que vous avez un répertoire comme:
lib/abc.py
Il suffit ensuite de garder un fichier vide dans le dossier lib nommé
__init__.py
Et puis utiliser
from lib.abc import <Your Module name>
Conservez le fichier __init__.py
dans chaque dossier de la hiérarchie du module d'importation.
Si vous structurez votre projet de cette façon:
src\
__init__.py
main.py
dirFoo\
__init__.py
Foo.py
dirBar\
__init__.py
Bar.py
Ensuite, à partir de Foo.py, vous devriez pouvoir faire:
import dirFoo.Foo
Ou:
from dirFoo.Foo import FooObject
Selon le commentaire de Tom, cela nécessite que le dossier src
soit accessible via site_packages
ou votre chemin de recherche. De plus, comme il le mentionne, __init__.py
est importé implicitement lors de la première importation d'un module dans ce paquet/répertoire. Typiquement, __init__.py
est simplement un fichier vide.
La méthode la plus simple consiste à utiliser sys.path.append ().
Cependant, vous pouvez également être intéressé par le module imp . Il donne accès aux fonctions d'importation internes.
# mod_name is the filename without the .py/.pyc extention
py_mod = imp.load_source(mod_name,filename_path) # Loads .py file
py_mod = imp.load_compiled(mod_name,filename_path) # Loads .pyc file
Cela peut être utilisé pour charger des modules de manière dynamique lorsque vous ne connaissez pas le nom d'un module.
J'ai déjà utilisé cela par le passé pour créer une interface de type plugin vers une application, dans laquelle l'utilisateur écrivait un script avec des fonctions spécifiques à l'application et déposait simplement son script dans un répertoire spécifique.
En outre, ces fonctions peuvent être utiles:
imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)
Voici le PEP pertinent:
http://www.python.org/dev/peps/pep-0328/
En particulier, supposer que dirFoo est un répertoire situé en amont de dirBar ...
Dans dirFoo\Foo.py:
from ..dirBar import Bar
Le moyen le plus simple, sans aucune modification de votre script, consiste à définir la variable d’environnement PYTHONPATH. Parce que sys.path est initialisé à partir de ces emplacements:
Il suffit de courir:
export PYTHONPATH=/absolute/path/to/your/module
Sys.path contiendra le chemin ci-dessus, comme indiqué ci-dessous:
print sys.path
['', '/absolute/path/to/your/module', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client', '/usr/lib/python2.7/dist-packages/ubuntuone-client', '/usr/lib/python2.7/dist-packages/ubuntuone-control-panel', '/usr/lib/python2.7/dist-packages/ubuntuone-couch', '/usr/lib/python2.7/dist-packages/ubuntuone-installer', '/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol']
A mon avis, le meilleur choix est de mettre __ init __. Py dans le dossier et d’appeler le fichier avec
from dirBar.Bar import *
Il n'est pas recommandé d'utiliser sys.path.append (), car quelque chose pourrait mal tourner si vous utilisez le même nom de fichier que le package python existant. Je n'ai pas testé cela, mais ce sera ambigu.
Si vous vous contentez de bricoler et que vous ne vous préoccupez pas des problèmes de déploiement, vous pouvez utiliser un lien symbolique (en supposant que votre système de fichiers le supporte) pour rendre le module ou le package directement visible dans le dossier du module demandeur.
ln -s (path)/module_name.py
ou
ln -s (path)/package_name
Remarque: Un "module" est un fichier avec une extension .py et un "package" est un dossier contenant le fichier __init__.py
(qui peut être un fichier vide). Du point de vue de l'utilisation, les modules et les packages sont identiques - les deux exposent leurs "définitions et instructions" contenues dans la requête, à l'aide de la commande import
.
from .dirBar import Bar
au lieu de:
from dirBar import Bar
juste au cas où il pourrait y avoir un autre dirBar installé et confondre un lecteur foo.py.
Pour ce cas, importer Bar.py dans Foo.py, tout d’abord, je transformerais ces dossiers en Python paquets comme ceci:
dirFoo\
__init__.py
Foo.py
dirBar\
__init__.py
Bar.py
Ensuite, je le ferais comme ceci dans Foo.py:
from .dirBar import Bar
Si je voulais que l'espace de noms ressemble à Bar .peu importe, ou
from . import dirBar
Si je voulais le namespacing dirBar.Bar .peu importe. Ce second cas est utile si vous avez plus de modules sous le paquet dirBar.
Ajoutez un fichier __ init __. Py:
dirFoo\
Foo.py
dirBar\
__init__.py
Bar.py
Ajoutez ensuite ce code au début de Foo.py:
import sys
sys.path.append('dirBar')
import Bar
Exemple relatif sys.path:
# /lib/my_module.py
# /src/test.py
if __== '__main__' and __package__ is None:
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
import my_module
Basé sur this réponse.
Comme vous le mentionnez, vous souhaitez généralement avoir accès à un dossier contenant vos modules par rapport à l'emplacement d'exécution de votre script principal. Vous ne devez donc les importer que.
Solution:
J'ai le script dans D:/Books/MyBooks.py
et quelques modules (comme oldies.py). J'ai besoin d'importer du sous-répertoire D:/Books/includes
:
import sys,site
site.addsitedir(sys.path[0] + '\\includes')
print (sys.path) # Just verify it is there
import oldies
Placez une print('done')
dans oldies.py
afin de vérifier que tout se passe bien. Cela fonctionne toujours parce que, par le Python definition sys.path
initialisé au démarrage du programme, le premier élément de cette liste, path[0]
, est le répertoire contenant le script utilisé pour appeler l'interprète Python.
Si le répertoire de script n'est pas disponible (par exemple, si l'interpréteur est appelé de manière interactive ou si le script est lu à partir de l'entrée standard), path[0]
est la chaîne vide, qui dirige Python vers les modules de recherche dans le répertoire. répertoire actuel en premier. Notez que le répertoire de script est inséré avant les entrées insérées à la suite de PYTHONPATH
.
Une autre solution consisterait à installer le package py-require , puis à utiliser les éléments suivants dans Foo.py
import require
Bar = require('./dirBar/Bar')
Vous pouvez simplement utiliser: from Desktop.filename import something
Exemple:
étant donné que le fichier s'appelle
test.py
dans le répertoireUsers/user/Desktop
, il importera tout.
le code:
from Desktop.test import *
Mais assurez-vous de créer un fichier vide appelé "__init__.py
" dans ce répertoire
Voici un moyen d'importer un fichier d'un niveau supérieur en utilisant le chemin relatif.
Fondamentalement, déplacez simplement le répertoire de travail d'un niveau (ou de tout emplacement relatif), ajoutez-le à votre chemin, puis déplacez le répertoire de travail à son point de départ.
#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path = os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)
Regardez le module pkgutil de la bibliothèque standard. Cela peut vous aider à faire ce que vous voulez.
Je n’ai pas d’expérience du python. Par conséquent, s’il ya quelque chose qui cloche, dites-le-moi simplement. Si votre hiérarchie de fichiers est organisée comme ceci:
project\
module_1.py
module_2.py
module_1.py
définit une fonction appelée func_1()
, module_2.py:
from module_1 import func_1
def func_2():
func_1()
if __== '__main__':
func_2()
et vous lancez python module_2.py
dans cmd, il exécutera ce que func_1()
définit. C'est généralement comme cela que nous importons les mêmes fichiers de hiérarchie. Mais lorsque vous écrivez from .module_1 import func_1
dans module_2.py
, python, l'interprète dira No module named '__main__.module_1'; '__main__' is not a package
. Donc, pour résoudre ce problème, nous conservons simplement les modifications que nous venons de faire, nous déplaçons les deux modules dans un package, puis nous appelons un troisième module en tant qu'appelant pour exécuter module_2.py
.
project\
package_1\
module_1.py
module_2.py
main.py
main.py:
from package_1.module_2 import func_2
def func_3():
func_2()
if __== '__main__':
func_3()
Mais la raison pour laquelle nous ajoutons un .
avant module_1
dans module_2.py
est que si nous ne le faisons pas et exécutons main.py
, python l'interprète dira No module named 'module_1'
, c'est un peu compliqué, module_1.py
est juste à côté de module_2.py
. Maintenant, je laisse func_1()
dans module_1.py
faire quelque chose:
def func_1():
print(__name__)
que __name__
enregistre qui appelle func_1. Maintenant, nous conservons le .
avant module_1
, exécutons main.py
, il imprimera package_1.module_1
et non module_1
. Il indique que celui qui appelle func_1()
est dans la même hiérarchie que main.py
, le .
implique que module_1
est dans la même hiérarchie que module_2.py
. Donc, s'il n'y a pas de point, main.py
reconnaîtra module_1
à la même hiérarchie que lui-même, il pourra reconnaître package_1
, mais pas ce qu'il "sous".
Rendons maintenant un peu compliqué. Vous avez un config.ini
et un module définit une fonction pour le lire dans la même hiérarchie que 'main.py'.
project\
package_1\
module_1.py
module_2.py
config.py
config.ini
main.py
Et pour une raison inévitable, vous devez l'appeler avec module_2.py
, il doit donc importer depuis la hiérarchie supérieure .module_2.py:
import ..config
pass
Deux points signifie une importation depuis la hiérarchie supérieure (trois points d’accès supérieur à supérieur, etc.). Maintenant, nous lançons main.py
, l'interprète dira: ValueError:attempted relative import beyond top-level package
. Le "paquetage de niveau supérieur" ici est main.py
. Juste parce que config.py
est à côté de main.py
, ils sont dans la même hiérarchie, config.py
n'est pas "sous" main.py
, ou il n'est pas "guidé" par main.py
, donc c'est au-delà de main.py
. Pour résoudre ce problème, le moyen le plus simple est:
project\
package_1\
module_1.py
module_2.py
config.py
config.ini
main.py
Je pense que cela coïncide avec le principe d’arrangement de la hiérarchie des fichiers de projet. Vous devez arranger les modules avec des fonctions différentes dans des dossiers différents, tout en laissant un interlocuteur principal à l’extérieur, et vous pouvez importer ce que vous voulez.