Importation de fichiers à partir d'un dossier différent

1410

J'ai la structure de dossiers suivante.

application/app/folder/file.py

et je veux importer certaines fonctions de file.py dans un autre fichier Python qui réside dans

application/app2/some_folder/some_file.py

J'ai essayé

from application.app.folder.file import func_name

et quelques autres tentatives diverses mais jusqu'à présent, je n'ai pas réussi à importer correctement. Comment puis-je faire ceci?

Ivan
la source
2
EN RELATION
La lecture de la documentation officielle m'a beaucoup aidé! docs.python.org/3/reference/…
Kavin Raju S

Réponses:

1447

Par défaut, vous ne pouvez pas. Lors de l'importation d'un fichier, Python ne recherche que le répertoire en cours, le répertoire à partir sys.pathduquel le script du point d'entrée s'exécute, et qui inclut des emplacements tels que le répertoire d'installation du package (c'est en fait un peu plus complexe que cela, mais cela couvre la plupart des cas) .

Cependant, vous pouvez ajouter au chemin Python lors de l'exécution:

# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')

import file
Cameron
la source
357
sys.path.append('/path/to/application/app/folder')est plus propre imo
pseudosudo
388
@pseudosudo: Oui, c'est vrai, mais l'insérer au début a l'avantage de garantir que le chemin est recherché avant les autres (même ceux intégrés) en cas de conflits de nommage.
Cameron
8
@kreativitea - sys.pathrenvoie un list, pas un deque, et il serait idiot de convertir le listen a dequeet en arrière.
ArtOfWarfare
37
Est-il considéré comme un moyen pythonique de gérer les fichiers .py dans des dossiers? Je me demande ... pourquoi ce n'est pas pris en charge par défaut? il n'est pas logique de conserver tous les fichiers .py dans un seul répertoire ..
Ofir
49
@Ofir: Non, ce n'est pas une belle solution pythonique propre. En général, vous devez utiliser des packages (qui sont basés sur des arborescences de répertoires). Cette réponse était spécifique à la question posée, et pour une raison quelconque continue d'accumuler un grand nombre de votes positifs.
Cameron
710

Rien de mal avec:

from application.app.folder.file import func_name

Assurez-vous simplement qu'il foldercontient également un __init__.py, cela lui permet d'être inclus en tant que package. Je ne sais pas pourquoi les autres réponses parlent PYTHONPATH.

joey
la source
47
Parce que cela ne couvre pas les cas où une modification PYTHONPATHest nécessaire. Supposons que vous ayez deux dossiers au même niveau: Aet B. Aa un __init.py__. Essayez d'importer quelque chose de l' Bintérieur A.
msvalkon
32
Que contient le fichier init.pyor __init__.py?
Lorem Ipsum Dolor
48
@Xinyang Il peut s'agir d'un fichier vide. Son existence même dit à Python de traiter le répertoire comme un package.
jay
9
Bien sûr, cette réponse suppose que applicationet appsont déjà des packages (c'est-à-dire que vous en avez déjà __init__.pydans les deux). À la suite de l'ajout __init__.pyégalement à folder, application.app.folderdevient un (sous) package , à partir duquel vous pouvez accéder au module application.app.folder.file , dont le symbole func_namepeut maintenant être importé
Yibo Yang
30
Quoi que j'essaye, cela ne fonctionnera pas. Je veux importer à partir d'un répertoire "frère", donc un vers le haut. Tous ont __ init __. Py, y compris le parent. Ce python 3 est-il spécifique?
dasWesen
111

Lorsque les modules sont dans des emplacements parallèles, comme dans la question:

application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py

Ce raccourci rend un module visible pour l'autre:

import sys
sys.path.append('../')
slizb
la source
24
Comme mise en garde: cela fonctionne tant que le script d'importation est exécuté à partir de son répertoire conteneur. Sinon, le répertoire parent de tout autre répertoire à partir duquel le script est exécuté sera ajouté au chemin d'accès et l'importation échouera.
Carl Smith
14
Pour éviter cela, nous pouvons obtenir le répertoire parent du fichiersys.path.append(os.path.dirname(os.path.abspath(__file__)))
Rahul
Cela n'a pas fonctionné pour moi - j'ai dû ajouter un nom de répertoire supplémentaire pour remonter vers le parent, afin que l'exécution à cli/foo.pypartir de la ligne de commande puisseimport cli.bar
RCross
2
@Rahul, votre solution ne fonctionne pas pour les shells interactifs
towi_parallelism
Si vous l'exécutez à partir de votre dossier racine (c.-à-d. Dossier d'application), vous êtes probablement d'accord avec l' sys.path.append('.')importation du module à l'aide de from app2.some_folder.some_file import your_function. Alternativement, ce qui fonctionne pour moi s'exécute à python3 -m app2.another_folder.another_filepartir du dossier racine.
accro
68

Premier import sys dans name-file.py

 import sys

Deuxième append le chemin du dossier dans name-file.py

sys.path.insert(0, '/the/folder/path/name-package/')

Troisièmement, créez un fichier vide appelé __ init __.py dans votre sous-répertoire (cela indique à Python qu'il s'agit d'un package)

  • nom-fichier.py
  • nom-package
    • __ init __.py
    • name-module.py

Quatrièmement, importez le module dans le dossier name-file.py

from name-package import name-module
Alex Montoya
la source
5
Le dossier de noms étant juste en dessous de nom-fichier.py, cela devrait fonctionner même sans la sys.path.insertcommande-. En tant que tel, la réponse laisse la question, si cette solution fonctionne même lorsque le dossier de noms est situé dans un emplacement arbitraire.
Bastian
55

Je pense qu'une manière ad-hoc serait d'utiliser la variable d'environnementPYTHONPATH comme décrit dans la documentation: Python2 , Python3

# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH

# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
Ax3l
la source
Attendez, est-ce que je remplacerais mes scripts par le nom de fichier?
Vladimir Poutine
4
non, avec le chemin du répertoire vers votre fichier .py
Ax3l
1
Malheureusement, si vous utilisez Anaconda, cela ne fonctionnera pas, car sous le capot, PYTHONPATH n'est pas vraiment utilisé en interne!
information_interchange
Pour les changements (récents) dans anaconda, consultez cet OS pour les flux de travail et les commentaires pour les solutions de contournement: stackoverflow.com/questions/17386880/… De manière générale, créez et installez de petits packages au lieu de pirater les répertoires d'importation.
Ax3l
47

Les réponses ici manquent de clarté, cela est testé sur Python 3.6

Avec cette structure de dossiers:

main.py
|
---- myfolder/myfile.py

myfile.pya le contenu:

def myfunc():
    print('hello')

L'instruction d'importation dans main.pyest:

from myfolder.myfile import myfunc
myfunc()

et cela imprimera bonjour .

danday74
la source
9
l'ajout d'un fichier de configuration init .py (vide) dans mon dossier a fonctionné pour moi sur linux (y)
Vincent
7
@Vincent, tu voulais dire __init__.py?
mrgloom
2
@mrgloom effet
Vincent
3
Pour une raison quelconque, l'ajout __init__.pyne fonctionne pas pour moi. J'utilise Py 3.6.5 sur Ubuntu 18. Il fonctionne sur Pycharm mais pas depuis le terminal
Crearo Rotar
9
Cela n'a aucun rapport avec la question qui se pose sur l'importation de fichiers à partir d'une branche de l'arborescence de fichiers différente de celle du répertoire de travail actuel.
Alexander Rossa
45

Votre problème est que Python recherche ce fichier dans le répertoire Python et ne le trouve pas. Vous devez spécifier que vous parlez du répertoire dans lequel vous vous trouvez et non du répertoire Python.

Pour ce faire, vous changez ceci:

from application.app.folder.file import func_name

pour ça:

from .application.app.folder.file import func_name

En ajoutant le point, vous dites recherchez dans ce dossier le dossier d'application au lieu de chercher dans le répertoire Python.

CianB
la source
2
c'est ce qui m'a fixé
Phi
32

D'après ce que je sais, ajouter un __init__.pyfichier directement dans le dossier des fonctions que vous souhaitez importer fera l'affaire.

Vaibhav Singh
la source
7
seulement si le script qui veut inclure cet autre répertoire est déjà dans le sys.path
Ax3l
2
J'ai utilisé sys.path.append(tools_dir)sur Windows et je n'ai pas besoin d'ajouter un __init__.py' file in my directory tools_dir`
herve-guerin
25

Dans Python 3.4 et versions ultérieures, vous pouvez importer directement à partir d'un fichier source (lien vers la documentation) . Ce n'est pas la solution la plus simple, mais j'inclus cette réponse pour être complet.

Voici un exemple. Tout d'abord, le fichier à importer, nommé foo.py:

def announce():
    print("Imported!")

Le code qui importe le fichier ci-dessus, largement inspiré de l'exemple de la documentation:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

foo = module_from_file("foo", "/path/to/foo.py")

if __name__ == "__main__":
    print(foo)
    print(dir(foo))
    foo.announce()

Le résultat:

<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

Notez que le nom de variable, le nom de module et le nom de fichier ne doivent pas nécessairement correspondre. Ce code fonctionne toujours:

import importlib.util

def module_from_file(module_name, file_path):
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

baz = module_from_file("bar", "/path/to/foo.py")

if __name__ == "__main__":
    print(baz)
    print(dir(baz))
    baz.announce()

Le résultat:

<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!

L'importation par programme de modules a été introduite dans Python 3.1 et vous donne plus de contrôle sur la façon dont les modules sont importés. Reportez-vous à la documentation pour plus d'informations.

wecsam
la source
11
Je ne sais pas si quelqu'un a même essayé de comprendre cela, mais je pense que c'est trop compliqué.
Dan
23

A travaillé pour moi en python3 sur linux

import sys  
sys.path.append(pathToFolderContainingScripts)  
from scriptName import functionName #scriptName without .py extension  
dsg38
la source
6
sys.path.append("/home/linux/folder/")- Assurez-vous de ne pas utiliser de raccourci, par exemple"~/folder/"
daGo
22

Essayez les importations relatives de Python:

from ...app.folder.file import func_name

Chaque point de tête est un autre niveau supérieur de la hiérarchie commençant par le répertoire courant.


Problèmes? Si cela ne fonctionne pas pour vous, vous obtenez probablement peu par les nombreuses importations relatives de gotcha. Lisez les réponses et les commentaires pour plus de détails: Comment corriger «Tentative d'importation relative dans un non-package» même avec __init__.py

Astuce: avoir __init__.pyà chaque niveau de répertoire. Vous pourriez avoir besoin python -m application.app2.some_folder.some_file(en laissant .py) que vous exécutez à partir du répertoire de niveau supérieur ou avoir ce répertoire de niveau supérieur dans votre PYTHONPATH. Phew!

Zectbumo
la source
22

J'ai été confronté au même défi, en particulier lors de l'importation de plusieurs fichiers, c'est ainsi que j'ai réussi à le surmonter.

import os, sys

from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))

from root_folder import file_name
Erick Mwazonga
la source
2
Votre réponse serait plus utile si vous pouviez expliquer ce qu'elle fait différemment d'une importation ordinaire?
not2qubit
1
J'avais /path/dir1/__init__.py et /path/dir1/mod.py. Pour /path/some.py de dir1.mod, la fonction d'importation a fonctionné. Dans /path/dir2/some.py, cela ne fonctionnait qu'après avoir copié et collé la réponse ci-dessus en haut du fichier. Je ne voulais pas modifier mon chemin, car tous les projets Python que j'ai ne se trouvent pas dans / path /.
jwal
19

Considérant applicationque le répertoire racine de votre projet python, créer un vide __init__.pyfichier application, appet des folderdossiers. Ensuite, dans vos some_file.pymodifications, procédez comme suit pour obtenir la définition de func_name:

import sys
sys.path.insert(0, r'/from/root/directory/application')

from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()
ChandanK
la source
devrait être: sys.path.insert (0, r '/ from / root / directory')
Bolaka
18

L'utilisation de sys.path.append avec un chemin absolu n'est pas idéale lors du déplacement de l'application vers d'autres environnements. L'utilisation d'un chemin relatif ne fonctionnera pas toujours car le répertoire de travail actuel dépend de la façon dont le script a été appelé.

La structure du dossier d'application étant fixe, nous pouvons utiliser os.path pour obtenir le chemin complet du module que nous souhaitons importer. Par exemple, si c'est la structure:

/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py

Et disons que vous souhaitez importer le module "mango". Vous pouvez faire ce qui suit dans vanilla.py:

import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango

Bien sûr, vous n'avez pas besoin de la variable mango_dir.

Pour comprendre comment cela fonctionne, regardez cet exemple de session interactive:

>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
    '/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>> 
>>> newdir
'/home/me/application/app2/another_folder'
>>> 

Et consultez la documentation os.path .

Nagev
la source
13

Cela fonctionne pour moi sur Windows

# some_file.py on mainApp/app2 
import sys
sys.path.insert(0, sys.path[0]+'\\app2')

import some_file
Emeeus
la source
10

Je suis assez spécial: j'utilise Python avec Windows!

Je viens de compléter les informations: pour Windows et Linux, les chemins relatifs et absolus fonctionnent sys.path(j'ai besoin de chemins relatifs car j'utilise mes scripts sur plusieurs PC et sous différents répertoires principaux).

Et lorsque vous utilisez Windows à la fois \et /pouvez être utilisé comme séparateur pour les noms de fichiers et bien sûr, vous devez doubler \en chaînes Python,
quelques exemples valides:

sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')

(note: je pense que /c'est plus pratique que \, événement s'il est moins «natif de Windows» car il est compatible Linux et plus simple à écrire et à copier dans l'explorateur Windows)

herve-guerin
la source
4
os.path.join ('tools', 'mydir')
Corey Goldberg
7

Si le but du chargement d'un module à partir d'un chemin spécifique est de vous aider lors du développement d'un module personnalisé, vous pouvez créer un lien symbolique dans le même dossier du script de test qui pointe vers la racine du module personnalisé. Cette référence de module aura priorité sur tous les autres modules installés du même nom pour tout script exécuté dans ce dossier.

J'ai testé cela sur Linux mais cela devrait fonctionner dans n'importe quel système d'exploitation moderne qui prend en charge les liens symboliques.

Un avantage de cette approche est que vous pouvez pointer vers un module qui se trouve dans votre propre copie de travail de branche SVC locale, ce qui peut considérablement simplifier le temps de cycle de développement et réduire les modes de défaillance de la gestion des différentes versions du module.

Timothy C. Quinn
la source
7

Dans mon cas, j'avais une classe à importer. Mon fichier ressemblait à ceci:

# /opt/path/to/code/log_helper.py
class LogHelper:
    # stuff here

Dans mon fichier principal, j'ai inclus le code via:

import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
schmudu
la source
1
@ not2qubit sys n'a pas été importé dans la réponse.
Walter
7

Je suis tombé sur la même question plusieurs fois, alors je voudrais partager ma solution.

Version Python: 3.X

La solution suivante est destinée à quelqu'un qui développe votre application dans Python version 3.X car Python 2 n'est pas pris en charge depuis le 1er janvier 2020 .

Structure du projet

En python 3, vous n'avez pas besoin __init__.pydans votre sous-répertoire de projet en raison des packages d'espace de noms implicites . Voir Is init .py n'est pas requis pour les packages en Python 3.3+

Project 
├── main.py
├── .gitignore
|
├── a
|   └── file_a.py
|
└── b
    └── file_b.py

Énoncé du problème

Dans file_b.py, je voudrais importer une classe Adans file_a.pyle dossier a.

Solutions

# 1 Un moyen rapide mais sale

Sans installer le package comme vous développez actuellement un nouveau projet

Utiliser le try catchpour vérifier si les erreurs. Exemple de code:

import sys
try:
    # The insertion index should be 1 because index 0 is this file
    sys.path.insert(1, '/absolute/path/to/folder/a')  # the type of path is string
    # because the system path already have the absolute path to folder a
    # so it can recognize file_a.py while searching 
    from file_a import A
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

# 2 Installez votre package

Une fois que vous avez installé votre application (dans cet article, le tutoriel d'installation n'est pas inclus)

Vous pouvez simplement

try:
    from __future__ import absolute_import
    # now it can reach class A of file_a.py in folder a 
    # by relative import
    from ..a.file_a import A  
except (ModuleNotFoundError, ImportError) as e:
    print("{} fileure".format(type(e)))
else:
    print("Import succeeded")

Bon codage!

WY Hsu
la source
pour plus d'informations sur les importations absolues
WY Hsu
3

Je travaillais sur un projet aque je voulais que les utilisateurs installent via pip install aavec la liste de fichiers suivante:

.
├── setup.py
├── MANIFEST.in
└── a
    ├── __init__.py
    ├── a.py
    └── b
        ├── __init__.py
        └── b.py

setup.py

from setuptools import setup

setup (
  name='a',
  version='0.0.1',
  packages=['a'],
  package_data={
    'a': ['b/*'],
  },
)

MANIFEST.in

recursive-include b *.*

a / init .py

from __future__ import absolute_import

from a.a import cats
import a.b

a / a.py

cats = 0

a / b / init .py

from __future__ import absolute_import

from a.b.b import dogs

a / b / b.py

dogs = 1

J'ai installé le module en exécutant ce qui suit à partir du répertoire avec MANIFEST.in:

python setup.py install

Ensuite, à partir d'un emplacement totalement différent sur mon système de fichiers, /moustache/armwrestlej'ai pu exécuter:

import a
dir(a)

Ce qui a confirmé a.catsqu'effectivement égal à 0 et a.b.dogseffectivement égal à 1, comme prévu.

duhaime
la source
2

Au lieu de simplement faire un import ..., faites ceci:

from <MySubFolder> import <MyFile>

MyFile est à l'intérieur de MySubFolder.

Galileoomega
la source
1

Vous pouvez actualiser le shell Python en appuyant sur f5, ou allez dans Exécuter-> Exécuter le module. De cette façon, vous n'avez pas à modifier le répertoire pour lire quelque chose dans le fichier. Python changera automatiquement le répertoire. Mais si vous souhaitez travailler avec différents fichiers d'un répertoire différent dans le shell Python, vous pouvez modifier le répertoire dans sys, comme Cameron l'a dit plus tôt.

IOstream
la source
1

Je venais donc de cliquer avec le bouton droit de la souris sur mon IDE et d'en ajouter un nouveau folderet je me demandais pourquoi je ne pouvais pas importer à partir de celui-ci. Plus tard, j'ai réalisé que je devais faire un clic droit et créer un package Python, et non un dossier de système de fichiers classique. Ou une méthode post-mortem consistant à ajouter un __init__.py(ce qui fait que python traite le dossier du système de fichiers comme un package) comme mentionné dans d'autres réponses. Ajouter cette réponse ici juste au cas où quelqu'un emprunterait cette voie.

mithunpaul
la source
1

Vous pouvez utiliser importlib pour importer des modules où vous souhaitez importer un module à partir d'un dossier à l'aide d'une chaîne comme ceci:

import importlib

scriptName = 'Snake'

script = importlib.import_module('Scripts\\.%s' % scriptName)

Cet exemple a un main.py qui est le code ci-dessus puis un dossier appelé Scripts et vous pouvez ensuite appeler ce dont vous avez besoin à partir de ce dossier en changeant la scriptNamevariable. Vous pouvez ensuite utiliser scriptpour faire référence à ce module. comme si j'ai une fonction appelée Hello()dans le module Snake, vous pouvez exécuter cette fonction en le faisant:

script.Hello()

J'ai testé cela en Python 3.6

Dextron
la source
0

J'ai eu ces problèmes plusieurs fois. Je suis souvent venu sur cette même page. Dans mon dernier problème, j'ai dû exécuter le à serverpartir d'un répertoire fixe, mais chaque fois que le débogage je voulais exécuter à partir de différents sous-répertoires.

import sys
sys.insert(1, /path) 

n'a PAS fonctionné pour moi car à différents modules j'ai dû lire différents fichiers * .csv qui étaient tous dans le même répertoire.

En fin de compte, ce qui a fonctionné pour moi n'était pas pythonique, je suppose, mais:

J'ai utilisé un if __main__ sur le module que je voulais déboguer , qui est exécuté à partir d'un chemin différent de celui habituel.

Donc:

# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
    os.chdir('/path/for/the/regularly/run/directory')
B Furtado
la source