Importer un fichier depuis un sous-répertoire?

456

J'ai un fichier appelé tester.py, situé sur /project.

/projecta un sous-répertoire appelé lib, avec un fichier appelé BoxTime.py:

/project/tester.py
/project/lib/BoxTime.py

Je souhaite importer BoxTimedepuis tester. J'ai essayé ceci:

import lib.BoxTime

Résultat:

Traceback (most recent call last):
  File "./tester.py", line 3, in <module>
    import lib.BoxTime
ImportError: No module named lib.BoxTime

Des idées sur la façon d'importer à BoxTimepartir du sous-répertoire?

ÉDITER

Le __init__.pyétait le problème, mais ne pas oublier de se référer à BoxTimecomme lib.BoxTime, ou de l' utilisation:

import lib.BoxTime as BT
...
BT.bt_function()
Adam Matan
la source

Réponses:

536

Jetez un œil à la documentation des packages (Section 6.4) ici: http://docs.python.org/tutorial/modules.html

En bref, vous devez mettre un fichier vierge nommé

__init__.py

dans le répertoire "lib".

Greg
la source
59
Pourquoi est-ce que ça sent hacky ? C'est la façon dont python marque les répertoires d'importation sûrs / disponibles.
IAbstract
7
Non seulement il marque les répertoires d'importation sûrs / disponibles, mais fournit également un moyen d'exécuter du code d'initialisation lors de l'importation d'un nom de répertoire.
Sadjad
32
Oui, c'est hacky et même sale, et à mon avis, la langue ne devrait pas imposer sa façon de charger des fichiers à travers le système de fichiers. En PHP, nous avons résolu le problème en laissant le code userland enregistrer plusieurs fonctions de chargement automatique qui sont appelées lorsqu'un espace de noms / classe est manquant. Ensuite, la communauté a produit la norme PSR-4 et Composer l'implémente, et de nos jours personne n'a à s'en soucier. Et pas de __init__fichiers stupides codés en dur (mais si vous le souhaitez, enregistrez simplement un crochet de chargement automatique! C'est la différence entre hacky et hackable ).
Morgan Touverey Quilling
4
@ AurélienOomsimport sys, os; sys.path.insert(0, os.path.abspath('..')); from sibling_package.hacks import HackyHackHack
jbowman
4
python est en désordre :)
Jimmy Pettersson
174
  • Créez un sous-répertoire nommé lib.
  • Créez un fichier vide nommé lib\__init__.py.
  • Dans lib\BoxTime.py, écrivez une fonction foo()comme celle-ci:

    def foo():
        print "foo!"
  • Dans votre code client dans le répertoire ci lib- dessus , écrivez:

    from lib import BoxTime
    BoxTime.foo()
  • Exécutez votre code client. Tu auras:

    foo!

Beaucoup plus tard - sous Linux, cela ressemblerait à ceci:

% cd ~/tmp
% mkdir lib
% touch lib/__init__.py
% cat > lib/BoxTime.py << EOF
heredoc> def foo():
heredoc>     print "foo!"
heredoc> EOF
% tree lib
lib
├── BoxTime.py
└── __init__.py

0 directories, 2 files
% python 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lib import BoxTime
>>> BoxTime.foo()
foo!
Hughdbrown
la source
2
Pourriez-vous fournir un lien vers la documentation Python où cela est expliqué? Merci!
Zenon
5
Rendons ce lien cliquable: docs.python.org/3/tutorial/modules.html#packages
Gabriel Staples le
Procédure pas à pas pour l'implémentation d'un packagelib
MasterControlProgram
veuillez noter: les sous-répertoires ne doivent pas contenir de tirets ou de points, mais les traits de soulignement sont valides. pour moi, cela ressemble aux mêmes restrictions que pour les autres noms de symboles, mais je ne l'ai pas encore creusé jusqu'au niveau de la documentation.
Alexander Stohr
souligne => python3 (trop tard pour éditer le commentaire)
Alexander Stohr
68

Vous pouvez essayer de l'insérer dans sys.path:

sys.path.insert(0, './lib')
import BoxTime
Kresimir
la source
11
C'est très bien si, pour une raison quelconque, vous ne pouvez pas ou ne voulez pas créer le fichier init .py.
jpihl
1
Cela fonctionne si vous exécutez python à partir du répertoire "project". Le "." est interprété par rapport à votre répertoire de travail actuel, et non par rapport au répertoire où se trouve le fichier que vous exécutez. Dites - vous cd /data, python ../project/tester.py. Alors ça ne marchera pas.
Morningstar
2
Cela a fonctionné pour moi. Je préfère cela à un fichier init .py, cela rend les instructions d'importation plus propres.
Taylor Evanson
5
Cela fonctionne beaucoup mieux et est la solution «correcte». init .py gâche des paquets comme boto qui ont leurs propres dossiers enfants avec des modules.
Dave Dopson
1
@jpihl Vous devez créer (au moins) un fichier empy nommé __init__.py pour autoriser les modules d'importation python à partir de ce dossier. J'ai essayé cette solution et fonctionne parfaitement (v2.7.6).
m3nda
31

J'écris ceci parce que tout le monde semble suggérer que vous devez créer un librépertoire.

Vous n'avez pas besoin de nommer votre sous-répertoire lib. Vous pouvez le nommer à anythingcondition d'y mettre un __init__.py.

Vous pouvez le faire en entrant la commande suivante dans un shell Linux:

$ touch anything/__init__.py 

Alors maintenant, vous avez cette structure:

$ ls anything/
__init__.py
mylib.py

$ ls
main.py

Ensuite , vous pouvez importer mylibdans main.pycomme ceci:

from anything import mylib 

mylib.myfun()

Vous pouvez également importer des fonctions et des classes comme celle-ci:

from anything.mylib import MyClass
from anything.mylib import myfun

instance = MyClass()
result = myfun()

Toute fonction ou classe variable que vous placez à l'intérieur __init__.pyest également accessible:

import anything

print(anything.myvar)

Ou comme ça:

from anything import myvar

print(myvar)
nurettin
la source
Ma structure de dossiers est utils\__init__.pyet utils\myfile.py. (Les utilitaires contiennent les deux fichiers) Voici comment j'essaie d'importer from utils.myfile import myMethod. Mais je comprends ModuleNotFoundError: No module named 'utils'. Qu'est-ce qui ne va pas? PS: J'utilise Djangoet j'essaye d'importer dans views.pyqui est au même niveau que le utilsdossier
Jagruti
Il est possible d'utiliser des chemins absolus lors de l'importation de modules et d'exécuter votre programme avecPYTHONPATH=. python path/to/program.py
nurettin
21

Votre répertoire lib contient-il un __init__.py fichier?

Python utilise __init__.pypour déterminer si un répertoire est un module.

Patauger
la source
16

Essayez import .lib.BoxTime. Pour plus d'informations, lisez l'importation relative dans PEP 328 .

drrlvn
la source
2
Je ne pense pas avoir déjà vu cette syntaxe utilisée auparavant. Y a-t-il une bonne raison (de ne pas) utiliser cette méthode?
tgray
2
Pourquoi n'était-ce pas la réponse. Bien sûr, si vous voulez faire la totalité des packages, vous devriez le faire. Mais ce n'était pas la question initiale.
Travis Griggs
Cela me donne: ValueError: tentative d'importation relative dans un non-package
Alex
5
Cela ne fonctionne que si le fichier à partir duquel vous importez fait lui-même partie d'un package. Sinon, vous recevrez l'erreur signalée par @Alex.
Jonathon Reinhart
8

Je fais cela qui couvre essentiellement tous les cas (assurez-vous que vous avez __init__.pydans le dossier / path / vers / votre / lib / dossier):

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/your/lib/folder")
import someFileNameWhichIsInTheFolder
...
somefile.foo()


Exemple:
Vous avez dans votre dossier de projet:

/root/myproject/app.py

Vous avez dans un autre dossier de projet:

/root/anotherproject/utils.py
/root/anotherproject/__init__.py

Vous voulez utiliser /root/anotherproject/utils.pyet appeler la fonction foo qui s'y trouve.

Vous écrivez donc dans app.py:

import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../anotherproject")
import utils

utils.foo()
Mercure
la source
2
si vous utilisez, os.pathvous voudrez probablement utiliser os.path.join((os.path.dirname(os.path.realpath(__file__)),'..','anotherproject')au lieu de coder en dur le «/» dans la concaténation de votre chemin.
cowbert
Pourquoi ne pouvez-vous pas simplement vous en "../anotherproject"passer os.path.dirname()?
Moshe Rabaev
@MosheRabaev - Il est recommandé d'utiliser les fonctions os.path. En cas d'écriture de "../anotherproject" et de déplacement du code vers le système d'exploitation Windows, le code se cassera! Les utilitaires os.path savent comment renvoyer le chemin correct en considérant l'OS sur lequel le code s'exécute. pour plus d'informations docs.python.org/2/library/os.path.html
Mercury
@MosheRabaev et si vous utilisez ".." sans le dirname(realpath(__file__)), alors il calculera le chemin par rapport à votre répertoire de travail actuel lorsque vous exécutez le script, et non par rapport à son emplacement.
TJ Ellis
5

Créez un fichier vide __init__.pydans le sous-répertoire / lib. Et ajoutez au début du code principal

from __future__ import absolute_import 

puis

import lib.BoxTime as BT
...
BT.bt_function()

ou mieux

from lib.BoxTime import bt_function
...
bt_function()
Mik
la source
0

Juste un ajout à ces réponses.

Si vous souhaitez importer tous les fichiers de tous les sous - répertoires , vous pouvez l'ajouter à la racine de votre fichier.

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

Et puis vous pouvez simplement importer des fichiers à partir des sous-répertoires comme si ces fichiers se trouvaient dans le répertoire courant.

Exemple de travail

Si j'ai le répertoire suivant avec des sous-répertoires dans mon projet ...

.
├── a.py
├── b.py
├── c.py
├── subdirectory_a
   ├── d.py
   └── e.py
├── subdirectory_b
   └── f.py
├── subdirectory_c
   └── g.py
└── subdirectory_d
    └── h.py

Je peux mettre le code suivant dans mon a.pyfichier

import sys, os
sys.path.extend([f'./{name}' for name in os.listdir(".") if os.path.isdir(name)])

# And then you can import files just as if these files are inside the current directory

import b
import c
import d
import e
import f
import g
import h

En d'autres termes, ce code fera abstraction du répertoire d'où provient le fichier.

Victor
la source
-1

/project/tester.py

/project/lib/BoxTime.py

créer un fichier vierge __init__.pysur toute la ligne jusqu'à ce que vous atteigniez le fichier

/project/lib/somefolder/BoxTime.py

#lib- besoins a deux éléments un __init__.pyet un répertoire nommé somefolder #somefoldera deux éléments boxtime.pyet__init__.py

Chaitanya Gk
la source
-3

essaye ça:

from lib import BoxTime

Orane
la source
8
sans aucune explication, ce n'est pas très utile.
Jean-François Fabre