Importer un module à partir d'un chemin relatif
Comment puis-je 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
en 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.
23 réponses
en supposant que vos deux répertoires sont de vrais paquets Python (ils contiennent le fichier __init__.py
), voici une solution sûre pour inclure des modules relativement à l'emplacement du script.
je suppose que vous voulez le faire, parce que vous avez besoin d'un ensemble de modules avec votre script. Je l'utilise en production dans plusieurs produits et fonctionne dans de nombreux scénarios spéciaux tels que: scripts appelés à partir d'un autre répertoire ou exécutés avec python execute au lieu de l'ouverture d'un nouvel interprète.
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 bonus, cette approche vous permet de forcer Python à utiliser votre module au lieu de ceux installés sur le système.
Attention! Je ne sais pas vraiment ce qui se passe quand le module courant est dans un fichier egg
. Il échoue probablement aussi.
assurez-vous que dirBar a le fichier __init__.py
-- Cela fait d'un répertoire un paquet Python.
vous pouvez aussi ajouter le sous-répertoire à votre chemin Python pour qu'il soit importé comme un 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
Juste faire des choses simples pour importer le .py fichier à partir d'un autre dossier.
disons que vous avez un répertoire comme:
lib/abc.py
alors il suffit de garder un fichier vide dans le dossier lib comme nommé
__init__.py
et ensuite utiliser
from lib.abc import <Your Module name>
conserver 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
puis de Foo.py vous devriez être en mesure de 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. En outre, comme il le mentionne, __init__.py
est implicitement importé lorsque vous importez un module dans ce paquet/répertoire. Typiquement __init__.py
est simplement un fichier vide.
la méthode la plus facile est d'utiliser sys.chemin.annexer.)(
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
peut être utilisé pour charger des modules dynamiquement lorsque vous ne connaissez pas le nom d'un module.
j'ai utilisé ceci dans le passé pour créer une interface de type plugin à une application, où l'utilisateur écrirait un script avec fonctions spécifiques à l'application, et il suffit de déposer le script dans un répertoire spécifique.
Aussi, ces fonctions peuvent être utiles:
imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)
il s'agit de la PEP pertinente:
http://www.python.org/dev/peps/pep-0328 /
en particulier, présumant que dirFoo est un répertoire à partir de dirBar...
In dirFoo\Foo.py:
from ..dirBar import Bar
la façon la plus simple sans aucune modification de votre script est de définir la variable d'environnement PYTHONPATH. Parce que sys.le chemin est initialisé à partir de ces emplacements:
- Le répertoire contenant le script d'entrée (ou le courant répertoire.)
- PYTHONPATH( une liste de noms de répertoires, avec le même syntaxe que la variable d'environnement PATH).
- la valeur par défaut dépendante de l'installation.
Just exécuter:
export PYTHONPATH=/absolute/path/to/your/module
Vous sys.le chemin 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']
À mon avis, le meilleur choix est de mettre __ init __.py dans le dossier et appelez le fichier avec
from dirBar.Bar import *
il n'est pas recommandé d'utiliser sys.chemin.append() parce que quelque chose pourrait mal tourné si vous utilisez le même nom de fichier que le paquet python. Je ne l'ai pas testé, mais ce sera ambigu.
le chemin rapide et sale pour les utilisateurs de Linux
si vous êtes juste en train de bricoler et que vous ne vous souciez 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 paquet directement visible dans le dossier du module demandeur.
ln -s (path)/module_name.py
ou
ln -s (path)/package_name
Note: un" module " est n'importe quel fichier avec un .PY extension et a "package" est un dossier qui contient le fichier __init__.py
(qui peut être un fichier vide). Du point de vue de l'utilisation, les modules et les paquets sont identiques -- tous deux exposent leurs "définitions et énoncés" contenus comme demandé par 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 une foo.py lecteur.
pour ce cas d'importation Bar.py en Foo.py, d'abord je transformerais ces dossiers en paquets Python comme ceci:
dirFoo\
__init__.py
Foo.py
dirBar\
__init__.py
Bar.py
alors je le ferais comme ceci en Foo.py:
from .dirBar import Bar
si je voulais que l'espacenom ressemble à un Bar. quelque soit , ou
"from . import dirBar
si je voulais le nom dirBar.Bar. quel que soit . Ce second cas est utile si vous avez plusieurs modules sous le paquet dirBar.
Ajouter un __init__.py fichier:
dirFoo\
Foo.py
dirBar\
__init__.py
Bar.py
puis ajouter ce code au début de Foo.py:
import sys
sys.path.append('dirBar')
import Bar
par rapport sys.exemple de chemin:
# /lib/my_module.py
# /src/test.py
if __name__ == '__main__' and __package__ is None:
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
import my_module
basé sur cette réponse .
bien, comme vous le mentionnez, habituellement vous voulez avoir accès à un dossier avec vos modules relatifs à l'endroit où votre script principal est exécuté, donc vous n'avez qu'à les importer.
Solution:
j'ai le script dans D:/Books/MyBooks.py
et certains modules (comme oldies.py). Je dois 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 un print('done')
dans oldies.py
, donc vous vérifiez que tout va bien. De cette façon fonctionne toujours parce que par la définition de Python sys.path
initialisée au démarrage du programme, le premier élément de cette liste, path[0]
, est le répertoire contenant le script qui a été utilisé pour invoquer L'interpréteur Python.
si le répertoire de script n'est pas disponible (par exemple si l'interpréteur est invoqué de manière interactive ou si le script est lu à partir d'une entrée standard), path[0]
est la chaîne vide, qui dirige Python vers les modules de recherche dans le répertoire courant en premier. Notez que le répertoire script est inséré avant les entrées insérées à la suite de PYTHONPATH
.
une autre solution serait d'installer le paquet PY-require puis d'utiliser ce qui suit dans Foo.py
import require
Bar = require('./dirBar/Bar')
regardez le module pkgutil de la bibliothèque standard. Il peut vous aider à faire ce que vous voulez.
Voici une façon d'importer un fichier à partir d'un niveau au-dessus, en utilisant le chemin relatif.
essentiellement, il suffit de déplacer le répertoire de travail à un niveau supérieur (ou n'importe quel emplacement relatif), d'ajouter cela à votre chemin, puis de déplacer le répertoire de travail à l'endroit où il a commencé.
#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path = os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)
vous pouvez simplement utiliser: from Desktop.filename import something
exemple:
étant donné que le fichier porte le nom
test.py
dans le répertoireUsers/user/Desktop
, et importera tout.
le code:
from Desktop.test import *
mais assurez-vous que vous faites un fichier vide appelé " __init__.py
" dans ce répertoire
Je n'ai pas l'expérience de python, donc s'il y a quelque chose de mal dans mes mots, dites-le moi. 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 __name__ == '__main__':
func_2()
et vous lancez python module_2.py
dans cmd, il fera tourner ce que func_1()
définit. C'est généralement comme ça qu'on importe les mêmes fichiers de hiérarchie. Mais quand vous écrivez from .module_1 import func_1
module_2.py
, interprète python dira No module named '__main__.module_1'; '__main__' is not a package
. Donc pour corriger cela, nous gardons juste le changement que nous faisons, et déplaçons les deux modules dans un paquet, et faisons un troisième module en tant qu'appelant pour lancer 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 __name__ == '__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 que nous n'exécutons pas main.py
, l'interpréteur python dira No module named 'module_1'
, c'est un peu délicat, 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__)
ce __name__
enregistre qui appelle func_1. Maintenant, nous gardons le .
avant module_1
, Lancez main.py
, il affichera package_1.module_1
, pas 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 à la même hiérarchie que module_2.py
lui-même. Donc, s'il n'y a pas de point, main.py
reconnaîtra module_1
à la même hiérarchie qu'elle-même, elle peut reconnaître package_1
, mais pas ce que "sous" elle.
rendons ça un peu compliqué. Vous avez un config.ini
et un module définit une fonction pour lire à 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
, elle doit donc être importée de la hiérarchie supérieure. module_2.py :
import ..config
pass
deux points signifie importation de la hiérarchie supérieure (trois points d'accès supérieur à Supérieur,et ainsi de suite). Maintenant on exécute main.py
, l'interprète dira: ValueError:attempted relative import beyond top-level package
. Le" paquet de haut niveau "ici est main.py
. Juste parce que config.py
est à côté de main.py
, ils sont à la même hiérarchie, config.py
n'est pas "sous" main.py
, ou il n'est pas "dirigé" par main.py
, donc c'est au-delà de main.py
. Pour corriger cela, la manière la plus simple est:
project\
package_1\
module_1.py
module_2.py
config.py
config.ini
main.py
je pense que c'est coïncider avec le principe d'organiser la hiérarchie de dossier de projet, vous devriez organiser des modules avec la fonction différente dans différents dossiers, et laissez juste un haut appelant à l'extérieur, et vous pouvez importer comment jamais vous voulez.
cela fonctionne aussi, et est beaucoup plus simple que n'importe quoi avec le sys
module:
with open("C:/yourpath/foobar.py") as f:
eval(f.read())
Appelez-moi excessivement prudent, mais j'aime rendre le mien plus portable parce qu'il est dangereux de supposer que les fichiers seront toujours au même endroit sur chaque ordinateur. Personnellement, j'ai le code pour rechercher le chemin du fichier en premier. J'utilise Linux pour que le mien ressemble à ceci:
import os, sys
from subprocess import Popen, PIPE
try:
path = Popen("find / -name 'file' -type f", shell=True, stdout=PIPE).stdout.read().splitlines()[0]
if not sys.path.__contains__(path):
sys.path.append(path)
except IndexError:
raise RuntimeError("You must have FILE to run this program!")
C'est bien sûr à moins que vous n'ayez l'intention de les emballer ensemble. Mais si c'est le cas, vous n'avez pas vraiment besoin de deux fichiers distincts de toute façon.