Supposons que j'ai un tas de fichiers Python sous un répertoire commun, mais à l'intérieur de celui-ci, un nombre arbitraire de sous-répertoires peut exister:
import_everything_under('first')
J'ai des fichiers arbitraires où je veux importer tous ces éléments. Manuellement, je pourrais faire:
import first.foo import first.bar import first.fizz.buzz import first.numbers.one import first.numbers.two
Mais à la place, j'aimerais pouvoir faire quelque chose comme:
/first/foo.py /first/bar.py /first/fizz/buzz.py /first/numbers/one.py /first/numbers/two.py
Je sais qu'une question similaire a déjà surgi: Importez récursivement tous les fichiers .py fichiers de tous les dossiers
Mais les réponses données et les doublons présumés ne répondent PAS à cette question.
Cette question a été signalée comme un doublon possible de: Comment charger tous les modules d'un dossier?
Encore une fois, cela ne répond PAS à cette question. La réponse donnée à cette question n'est pas récursive - elle importera UNIQUEMENT des éléments du répertoire immédiat et n'inclut PAS les scripts dans les sous-répertoires, dont j'ai besoin pour mon cas d'utilisation.
3 Réponses :
Je suis désolé. Je ne sais pas si c'est bien de cette façon. Mais essayez ça.
de la première importation *
Cela n'importera pas récursivement à partir de sous-répertoires et ne répond donc pas aux besoins de cette question.
Votre problème se décompose en deux étapes (de mon point de vue):
Parcourez vos répertoires et sous-répertoires et trouvez les noms de tous les fichiers .py que vous souhaitez importer
Importez-les!
Pour résoudre (1), nous pouvons utiliser la méthode os.walk pour trouver tous les fichiers .py. Cette fonction ressemblerait à ceci:
import os
import importlib
def get_py_files(src):
cwd = os.getcwd() # Current Working directory
py_files = []
for root, dirs, files in os.walk(src):
for file in files:
if file.endswith(".py"):
py_files.append(os.path.join(cwd, root, file))
return py_files
def dynamic_import(module_name, py_path):
module_spec = importlib.util.spec_from_file_location(module_name, py_path)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
def dynamic_import_from_src(src, star_import = False):
my_py_files = get_py_files(src)
for py_file in my_py_files:
module_name = os.path.split(py_file)[-1].strip(".py")
imported_module = dynamic_import(module_name, py_file)
if star_import:
for obj in dir(imported_module):
globals()[obj] = imported_module.__dict__[obj]
else:
globals()[module_name] = imported_module
return
if __name__ == "__main__":
dynamic_import_from_src("first", star_import = False)
Maintenant que vous avez une liste de vos fichiers .py, nous avons besoin d'une fonction qui peut les importer dynamiquement.
def dynamic_import_from_src(src, star_import = False):
my_py_files = get_py_files(src)
for py_file in my_py_files:
module_name = os.path.split(py_file)[-1].strip(".py")
imported_module = dynamic_import(module_name, py_file)
if star_import:
for obj in dir(imported_module):
globals()[obj] = imported_module.__dict__[obj]
else:
globals()[module_name] = imported_module
return
Et maintenant nous avons juste besoin de tout rassembler, en écrivant une fonction qui appelle votre fonction get_py_files , puis en boucle sur les résultats, en chargeant le modules avec dynamic_import .
Je suppose que vous voulez que les noms de module que vous utilisez dans votre script python soient les mêmes que le nom de fichier, mais vous pouvez changer cela en modifiant le module_name variable dans la fonction ci-dessous.
import importlib
def dynamic_import(module_name, py_path):
module_spec = importlib.util.spec_from_file_location(module_name, py_path)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
Notez que nous devons appeler globals () pour ajouter le module à l'espace de noms global. Sans cela, le module sera importé mais vous n'aurez aucun moyen d'accéder à quoi que ce soit à l'intérieur. Vous pouvez passer star_import = True à dynamic_import_from_src si vous souhaitez plutôt qu'il s'agisse d'une importation en étoile. (comme from first.foo import * . Notez que cela peut écraser des variables dans votre espace de noms, mais c'est l'un des inconvénients de l'utilisation de l'importation * de toute façon.
Tout jeter dans un seul bloc pour qu'il soit plus facile de tout voir à la fois:
import os
def get_py_files(src):
cwd = os.getcwd() # Current Working directory
py_files = []
for root, dirs, files in os.walk(src):
for file in files:
if file.endswith(".py"):
py_files.append(os.path.join(cwd, root, file))
return py_files
À ce stade, vous pouvez accéder à tous les modules que vous avez importés exactement de la même manière comme si vous aviez importé en premier.quelque soit . Par exemple, si first / numbers / one.py contenait x = 1 , alors vous seriez capable d'y accéder en disant one.x.
Je vous remercie! Cela semble fonctionner. Marqué comme accepté.
@ glenn.barker J'ai apporté quelques modifications. J'ai accidentellement écrasé le mot-clé intégré, dir dans certaines fonctions, alors je l'ai remplacé par src à la place. Ajout de la possibilité de simuler les importations * dans dynamic_import_from_src
Merci beaucoup. Vraiment apprécié. J'ai également trouvé ma propre solution pendant que vous tapiez la vôtre, alors j'ai également partagé ce que j'ai trouvé comme réponse séparée. Je pense que l'une ou l'autre solution devrait fonctionner ici, nous devrons donc inclure les deux pour référence future. :-)
La solution de @SyntaxVoid devrait fonctionner mais en parallèle j'ai bricolé moi-même ce problème et je pense avoir trouvé une autre façon de le faire. Soit ma propre solution, soit la solution de @ SyntaxVoid devrait fonctionner, je crois.
from importlib import util
from os import path
from glob import glob
def import_submodules(start_path, include_start_directory=True):
start_path = path.abspath(start_path)
pattern = '**/*.py' if include_start_directory else '*/**/*.py'
py_files = [f for f in glob(path.join(start_path, pattern), recursive=True) if not f.endswith('__.py')]
for py_file in py_files:
spec = util.spec_from_file_location('', py_file)
module = util.module_from_spec(spec)
spec.loader.exec_module(module)
Copie possible de Comment charger tous les modules dans un dossier?
Pourquoi voudriez-vous faire ça? Qu'en est-il de la création de
d'abord / __ init__.pypuis d'y importer tous les modules pertinents (+ de même pour tous les sous-modules)?@a_guest Je sais que c'est certainement un cas d'utilisation inhabituel, mais cela est dû à la façon dont un cadre de dépendance que j'utilise est mis en place. Essentiellement, il permet à une sorte de système de "plugin" où il charge des scripts d'affecter son comportement à partir d'un répertoire spécifique - mais UNIQUEMENT ce répertoire. Il n'autorise pas les sous-répertoires, ce qui rend l'organisation à grande échelle très pénible. Je veux donc un seul script dans ce répertoire principal qui puisse charger dynamiquement mes propres scripts à partir de toute structure de répertoire arbitraire que je définis. Je pourrais simplement importer "manuellement" tous les modules dont j'ai besoin, mais c'est aussi une corvée de maintenance.