Importer un module à partir d'un chemin relatif

759

Comment importer un module Python compte tenu de son chemin d'accès relatif?

Par exemple, si dirFoocontient Foo.pyet dirBar, et dirBarcontient Bar.py, comment importer Bar.pydans Foo.py?

Voici une représentation visuelle:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

Foosouhaite inclure Bar, mais la restructuration de la hiérarchie des dossiers n'est pas une option.

Jude Allred
la source
2
On dirait stackoverflow.com/questions/72852/… , peut-être?
Joril
3
Vérifiez ma réponse, elle est la plus complète à ce jour, d'autres ne fonctionnent pas dans des cas particuliers, par exemple lorsque vous appelez le script à partir d'un autre répertoire ou d'un autre script python. Voir stackoverflow.com/questions/279237/…
sorin
J'ai eu un problème similaire et j'ai trouvé ça et ça marche !! apt-get install python-profiler
ldcl289
5
Juste au cas où quelqu'un voudrait le faire statiquement et arriverait ici (comme je l'ai fait :) vous pouvez également configurer la variable d'environnement
PYTHONPATH
Il vaut mieux suivre les instructions dans Lib / site.py pour chaque cas
Avenida Gez

Réponses:

333

En supposant que vos deux répertoires sont de vrais packages Python (ils contiennent le __init__.pyfichier), voici une solution sûre pour l'inclusion de modules relativement à l'emplacement du script.

Je suppose que vous voulez le faire, car vous devez inclure un ensemble de modules avec votre script. Je l'utilise en production dans plusieurs produits et fonctionne dans de nombreux scénarios spéciaux comme: des scripts appelés à partir d'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 au lieu de ceux installés sur le système.

Attention! Je ne sais pas vraiment ce qui se passe lorsque le module actuel se trouve dans un eggfichier. Il échoue probablement aussi.

Sorin
la source
5
puis-je obtenir une explication sur la façon dont cela fonctionne? J'ai un problème similaire et
j'AIMERAIS
En exécutant Win 7 Pro 64x et Python 2.7, j'obtiens quelques erreurs. 1) J'ai dû ajouter inspect à la liste d'importation. 2) La 1ère valeur, [0], dans le tuple est une chaîne vide. Le 2ème, [1], affiche le nom du fichier. Je suppose que le premier devrait être le chemin ... Des idées?
Adam Lewis
2
Si vous optez pour un sous-dossier, utilisez-le comme ceci: os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]) + "/subfolder")N'ajoutez PAS le sous-dossier auparavant abspathcar cela provoque de graves bogues.
Maximilian Hils
4
@ scr4ve vous devez utiliser os.path.join () à la place, et vous êtes invités à ajouter le case ( cmd_subfolder) directement à ma réponse. Merci!
sorin
7
pour moi realpathgénère déjà des chemins absolus, donc je n'ai pas besoin abspath. os.path.dirnamePeut également être utilisé au lieu de scinder, rendant l'indexation [0]obsolète. La ligne serait alors:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
ted
328

Assurez-vous que dirBar a le __init__.pyfichier - cela fait un répertoire dans un package Python.

S.Lott
la source
204
Notez que ce fichier peut être complètement vide.
Harley Holcombe
47
Si le répertoire parent de dirBar n'est pas dans, sys.pathalors la présence de __init__.pydans le dirBarrépertoire n'aide pas beaucoup.
jfs
7
-1, l'ajout __init.py__ne fonctionnera que lorsque le répertoire est déjà dans sys.path et dans mon cas, ce n'était pas le cas. La solution de "sorin" (acceptée) fonctionne toujours.
Czarek Tomczak
12
msgstr "lorsque le répertoire est déjà dans sys.path". Bien que tout à fait vrai, comment pourrions-nous deviner que le répertoire n'était pas en sys.pathprovenance de la question? Peut-être y a-t-il eu quelque chose d'omis que nous n'avons pas vu ou connu?
S.Lott
4
Ce n'est en aucun cas la réponse à la question: que le fichier d'initialisation soit là ou non, cela ne fait pas regarder python dans les sous-répertoires. Comment at-il obtenu des centaines de votes positifs?
Gented
261

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
Andrew Cox
la source
6
Il semble que votre réponse ne fonctionne pas avec des chemins relatifs, voir stackoverflow.com/questions/279237/…
bogdan
24
Cela fait travailler avec des chemins relatifs - il vous suffit de comprendre qu'un chemin relatif dépendra de ce que vous exécutez répertoire à partir, ce qui en fait une mauvaise solution pour tout autre qu'un hack.
nobar
12
Vous pouvez faire quelque chose commesys.path.append(os.path.dirname(__file__) + "/relative/path/to/module")
Falko
ousys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
vim
1
Pensez à utiliser sys.path.insert(0, <path to dirFoo>)car il chargera ce module avant les modules du même nom stockés ailleurs.
Anton Tarasenko
117
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule
lefakir
la source
2
J'aime ça parce que vous avez la possibilité de monter un répertoire.
Charles L.
21
Vous devriez utiliser os.path.join()au lieu de rejoindre par '/', qui se brisera dans les fenêtres (boiteuses).
0xc0de
18
Ce n'est pas fiable. Cela dépend de ce que le répertoire de travail actuel est, pas du répertoire dans lequel se trouve le script.
jamesdlin
Peut vouloir ajouter seulement si le chemin n'est pas déjà dans le chemin. lib_path = os.path.abspath ('../ fonctions') si lib_path n'est pas dans sys.path: sys.path.append (lib_path)
Brent
en réponse à @jamesdlin combinant quelques réponses: qu'en est-il os.path.abspath(os.path.join(__file__,'..','lib'))?
Matthew Davis
107

Faites simplement 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

Ensuite, gardez simplement un fichier vide dans le dossier lib comme nommé

__init__.py

Et puis utilisez

from lib.abc import <Your Module name>

Conservez le __init__.pyfichier dans chaque dossier de la hiérarchie du module d'importation.

Deepak 'Kaseriya'
la source
79

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 srcdossier soit accessible via site_packagesou votre chemin de recherche. De plus, comme il le mentionne, __init__.pyest implicitement importé lorsque vous importez un module dans ce package / répertoire pour la première fois. Il __init__.pys'agit généralement d' un fichier vide.

bouvard
la source
Mentionnez également que init .py est importé lorsque le premier module à l'intérieur de ce package est importé. De plus, votre exemple ne fonctionnera que si src se trouve dans le site_packages (ou dans le chemin de recherche)
Tom Leys
3
C'est la solution la plus simple que je cherchais. Si le fichier à importer est sûr d'être dans l'un des sous-répertoires, cette solution est un joyau.
Deepak GM
J'ai essayé la même chose et j'ai échoué. Je ne sais pas pourquoi. ImportError: Aucun module nommé customMath
Ming Li
9
Pourquoi quelqu'un voudrait-il importer Foo depuis Foo.py lui-même? Devrait être de Bar.py, je suppose.
bhaskarc
from dirFoo import Foodire Foo.bla(). Si vous en utilisez, import dirFoo.Foovous devez utiliser dirFoo.Foo.bla()- assez moche même sans étui à chameau.
Cees Timmerman
45

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 dynamiquement lorsque vous ne connaissez pas le nom d'un module.

J'ai utilisé cela dans le passé pour créer une interface de type plugin pour une application, où l'utilisateur écrirait un script avec des fonctions spécifiques à l'application, et déposerait simplement son script dans un répertoire spécifique.

De plus, ces fonctions peuvent être utiles:

imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)
monkut
la source
Notez que dans la documentation imp.load_source et imp.load_compiled sont répertoriés comme obsolètes. imp.find_module et imp.load_module sont recommandés à la place.
amicitas
@amicitas, pouvez-vous s'il vous plaît fournir une référence pour cela (j'en ai besoin, et j'utilise python 2.6. Je sais ce que 2.7 docs en disent, mais je n'ai trouvé aucune référence concernant 2.6)
0xc0de
@ 0xc0de Vous pouvez trouver cette instruction dans la documentation du module imp pour python 2.7.3 et python 2.6.7 . Il semble que ces fonctions ne figurent même pas dans la documentation de python 3.2.
amicitas
1
Pour importer des fichiers source comme celui-ci dans python 3.3, voir Importer un fichier source python abitrary. (Python 3.3+) - Débordement de pile . Merci à tous pour les conseils ici.
nealmcb
44

C'est le PEP pertinent:

http://www.python.org/dev/peps/pep-0328/

En particulier, en supposant que dirFoo est un répertoire à partir de dirBar ...

Dans dirFoo \ Foo.py:

from ..dirBar import Bar
Peter Crabtree
la source
cela a fonctionné pour moi, juste ajouté init .py sur chaque dossier et succès à importer
yussan
23

Le moyen le plus simple sans aucune modification de votre script est de définir la variable d'environnement PYTHONPATH. Parce que sys.path est initialisé à partir de ces emplacements:

  1. Le répertoire contenant le script d'entrée (ou le répertoire courant).
  2. PYTHONPATH (une liste de noms de répertoires, avec la même syntaxe que la variable shell PATH).
  3. Valeur par défaut dépendante de l'installation.

Exécutez simplement:

export PYTHONPATH=/absolute/path/to/your/module

Votre 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']
James Gan
la source
13

À 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.

jhana
la source
bizarre qui from dirBar.Bar import *fonctionne, mais pas from dirBar.Bar import Bar. savez-vous pourquoi * fonctionne? Et si j'avais plusieurs fichiers dans dirBar / et que je ne voulais en saisir que quelques-uns (en utilisant une méthode comme celle que vous avez publiée ici)?
testeur
2
@tester: Utilisez from dirBar import Bar.
nobar
@tester c'est parce que le fromindique la source, et tout importce qui se trouve après est ce qu'il faut récupérer de cette source. from dirBar.Bar import Barsignifie "De la source, importez la source elle-même", ce qui n'a pas de sens. le *bien signifie, "donnez-moi tout de la source"
FuriousFolder
11

Le moyen rapide et sale pour les utilisateurs de Linux

Si vous ne faites que 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 prend en charge) 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 n'importe quel fichier avec une extension .py et un "package" est n'importe quel dossier qui contient 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 comme demandé via la importcommande.

Voir: http://docs.python.org/2/tutorial/modules.html

nobar
la source
10
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.

jgomo3
la source
2
Je n'ai pas pu le faire sur Windows. Est-ce sur Linux?
Gabriel
1
Cela fonctionnera à partir d'un script important Foo. c'est-à-dire: main.py importe dirFoo.Foo. Si vous essayez d'exécuter Foo.py en tant que script, il échouera. Voir stackoverflow.com/questions/72852/…
jgomo3
9

Pour que ce cas importe Bar.py dans Foo.py, je transformerais d'abord ces dossiers en packages Python comme ceci:

dirFoo\
    __init__.py
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Ensuite, je le ferais comme ça dans Foo.py:

from .dirBar import Bar

Si je voulais que l'espace de noms ressemble à Bar. quoi que ce soit , ou

from . import dirBar

Si je voulais le dirBar.Bar d'espacement de noms. peu importe . Ce deuxième cas est utile si vous avez plus de modules sous le package dirBar.

Al Conrad
la source
7

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
Josh
la source
5
Si dirBarest déjà un paquet Python (par l'existence de dirBar/__init__.py), il n'y a pas besoin d'ajouter dirBarà sys.path, non? La déclaration import Barde Foo.pydevrait suffire.
Santa
5

Exemple relatif de sys.path:

# /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.

Der_Meister
la source
5

Eh bien, 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, il vous suffit donc de les importer.

Solution:

J'ai le script D:/Books/MyBooks.pyet quelques modules (comme oldies.py). Je dois importer du sous D:/Books/includes- répertoire :

import sys,site
site.addsitedir(sys.path[0] + '\\includes')
print (sys.path)  # Just verify it is there
import oldies

Placez un print('done')in oldies.pypour vérifier que tout se passe bien. Cette méthode fonctionne toujours car, selon la définition Python sys.pathtelle qu'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 appeler l'interpréteur 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 d'une entrée standard), path[0]est la chaîne vide, qui dirige Python pour rechercher les modules dans le 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.

Avenida Gez
la source
1
J'ai dû utiliser une barre oblique au lieu de deux barres obliques inversées (c'est-à-dire site.addsitedir(sys.path[0]+'/includes')) dans mon premier programme Python simple break_time.py: https://github.com/ltfschoen/PythonTest . J'utilise le système: MacOS v10.11.5, Python 2.7.12, IDLE IDE 2.7.12, Tk 8.5.9
Luke Schoen
4

Vous pouvez simplement utiliser: from Desktop.filename import something

Exemple:

étant donné que le fichier est nom test.pydans le répertoire Users/user/Desktop, et importera tout.

le code:

from Desktop.test import *

Mais assurez-vous de créer un fichier vide appelé " __init__.py" dans ce répertoire

0x1996
la source
1
les importations étoilées sont déconseillées. voir: stackoverflow.com/questions/2386714/why-is-import-bad
axolotl
Je sais que c'est pourquoi j'ai d'abord écrit, import somethingpuis je l'ai dit pour le rendre plus facile, * ce n'est pas bon pour le ram et aussi si 2 fonctions ont le même nom, il
massera
3

Une autre solution consisterait à installer le package py-require , puis à utiliser ce qui suit dansFoo.py

import require
Bar = require('./dirBar/Bar')
Niklas R
la source
L'URL semble en fait être pypi.org/project/require.py , et notez qu'elle doit être installée via Pip.
Flash Sheridan
1
@FlashSheridan Nope, c'est un projet différent. J'ai supprimé py-require car j'étais certain que personne ne l'utilisait, je n'ai pas pensé à ce post cependant. Si vous avez toujours besoin d'une require()fonction, vous pouvez jeter un œil à mon projet Node.py
Niklas R
2

Voici un moyen d'importer un fichier d'un niveau supérieur, en utilisant le chemin relatif.

Fondamentalement, il suffit de déplacer le répertoire de travail d'un niveau (ou de n'importe quel emplacement relatif), d'ajouter cela à votre chemin, puis de déplacer le répertoire de travail là 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)
Justin Muller
la source
1
je ne comprends pas votre logique ici. c'est trop compliqué
transilvlad
0

Je ne connais pas le python, donc s'il y a un problème 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.pydé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 exécutez python module_2.pyen cmd, il exécutera ce qui func_1()définit. C'est généralement ainsi que nous importons les mêmes fichiers de hiérarchie. Mais quand vous écrivez from .module_1 import func_1dans module_2.py, interpréteur Python 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 d'apporter, et déplaçons les deux modules dans un package, et créons un troisième module en tant qu'appelant à 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 __name__ == '__main__':
    func_3()

Mais la raison pour laquelle nous ajoutons un .avant module_1en module_2.pyest que si nous ne le faisons pas et exécuter main.py, l' interpréteur python va dire No module named 'module_1', c'est un peu délicat, qui module_1.pyest juste à côté module_2.py. Maintenant , je laisse func_1()en module_1.pydo quelque chose:

def func_1():
    print(__name__)

qui __name__enregistre qui appelle func_1. Maintenant, nous gardons l' .avant module_1, exécutons main.py, il imprimera package_1.module_1, non module_1. Cela indique que celui qui appelle func_1()est dans la même hiérarchie que main.py, .cela implique qu'il module_1est dans la même hiérarchie que module_2.pylui. Donc, s'il n'y a pas de point, main.pyreconnaîtra module_1la même hiérarchie que lui, il peut reconnaîtrepackage_1 , mais pas ce qui est "sous".

Maintenant, faisons un peu compliqué. Vous avez un config.iniet 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 de la hiérarchie supérieure. module_2.py :

 import ..config
 pass

Deux points signifie l'importation à partir de la hiérarchie supérieure (trois points d'accès supérieur à supérieur, etc.). Maintenant , nous courons main.py, l'interprète dira: ValueError:attempted relative import beyond top-level package. Le "package de niveau supérieur" ici est main.py. Tout simplement parce qu'ils sont à config.pycôté main.py, ils sont dans la même hiérarchie, config.pyne sont pas "sous" main.py, ou ils ne sont pas "dirigés" par main.py, donc c'est au-delà 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 de l'organisation de la hiérarchie des fichiers de projet, vous devez organiser les modules avec différentes fonctions dans différents dossiers, et laisser simplement un appelant supérieur à l'extérieur, et vous pouvez importer comme vous le souhaitez.

Findon Fassbender
la source
-5

Cela fonctionne également, et est beaucoup plus simple que tout avec le sysmodule:

with open("C:/yourpath/foobar.py") as f:
    eval(f.read())
jgilley
la source
1
Si OP devait coder en dur un chemin, ils pourraient tout de même insérer ce chemin dans le PYTHONPATH. Je pense que le but est de le faire d'une manière qui ne code pas en dur un chemin car il se briserait ailleurs.
Matthew
-15

Appelez-moi trop prudent, mais j'aime rendre le mien plus portable car il n'est pas sûr de supposer que les fichiers seront toujours au même endroit sur chaque ordinateur. Personnellement, le code recherche d'abord le chemin du fichier. J'utilise Linux donc le mien ressemblerait à 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 ne prévoyiez de les regrouper. Mais si c'est le cas, vous n'avez pas vraiment besoin de deux fichiers distincts de toute façon.

SuperFamousGuy
la source
11
Super inefficace!
0xc0de
1
Les fichiers seront toujours au même endroit sur chaque ordinateur, étant donné un chemin relatif.
Sagar Hatekar