module d'importation à partir d'une variable chaîne

183

Je travaille sur une documentation (personnelle) pour la bibliothèque imbriquée matplotlib (MPL), qui diffère de celle fournie par MPL, par les paquets de sous-modules intéressés. J'écris un script Python qui, j'espère, automatisera la génération de documents à partir des futures versions de MPL.
J'ai sélectionné les sous-modules / packages intéressés et je veux lister leurs classes principales à partir desquelles je vais générer la liste et la traiter avecpydoc

Le problème est que je ne trouve pas de moyen de demander à Python de charger un sous-module à partir d'une chaîne. Voici un exemple de ce que j'ai essayé:

import matplotlib.text as text
x = dir(text)

.

i = __import__('matplotlib.text')
y = dir(i)

.

j = __import__('matplotlib')
z = dir(j)

Et voici une comparaison à 3 voies des listes ci-dessus via pprint:

entrez la description de l'image ici

Je ne comprends pas ce qui est chargé dans l' yobjet - c'est la base matplotlibplus quelque chose d'autre, mais il manque les informations que je voulais et ce sont les classes principales du matplotlib.textpackage. C'est la partie supérieure de couleur bleue sur la capture d'écran ( xliste)

Veuillez ne pas suggérer Sphinx comme approche différente.

thêta
la source
Pouvez-vous expliquer pourquoi vous devez utiliser __import__(str)plutôt que le importstatemetn standard ?
thesamet
C'est parce que je vais traiter les listes d'éléments qui sont des sous-modules MPL et obtenir leurs chemins de méthodes
thêta
9
@thesamet - allez-y - il y a des idées infinies où vous voudriez cette fonctionnalité. Lorsque vous avez une configuration textuelle de bibliothèques, vous pouvez les charger par nom, ce qui ne fonctionnerait pas tout à fait avec l' importinstruction. Voici un exemple d'utilisation: djangosnippets.org/snippets/3048
Tomasz Gandor

Réponses:

279

La __import__fonction peut être un peu difficile à comprendre.

Si vous changez

i = __import__('matplotlib.text')

à

i = __import__('matplotlib.text', fromlist=[''])

alors ifera référence à matplotlib.text.

Dans Python 2.7 et Python 3.1 ou version ultérieure, vous pouvez utiliser importlib:

import importlib

i = importlib.import_module("matplotlib.text")

Quelques notes

  • Si vous essayez d'importer quelque chose à partir d'un sous-dossier, par exemple ./feature/email.py, le code ressemblera àimportlib.import_module("feature.email")

  • Vous ne pouvez rien importer s'il n'y en a pas __init__.pydans le dossier contenant le fichier que vous essayez d'importer

mzjn
la source
3
importlibdevrait être disponible sur pypi pour <python2.7
Jeffrey Jose
50
Pour tous ceux qui viennent ici de Google. Il convient de noter que si vous essayez d'importer quelque chose à partir d'un sous-dossier (par exemple, ./feature/email.py) le code ressemblera àimportlib.import_module("feature.email")
Seanny123
11
Enfin, rappelez-vous également que vous ne pouvez rien importer s'il n'y en a pas __init__.pydans le dossier contenant le fichier que vous essayez d'importer.
Seanny123
3
@mzjn Ceci est pour import moduleNameoù moduleName est une chaîne. Et pourquoi pas from moduleName import *?
Nam G VU
2
Je viens de trouver la réponse à ma question ici au cas où quelqu'un en aurait besoin stackoverflow.com/a/31306598/248616
Nam G VU
68

importlib.import_moduleest ce que vous recherchez. Il renvoie le module importé. (Uniquement disponible pour Python> = 2.7 ou 3.x):

import importlib

mymodule = importlib.import_module('matplotlib.text')

Vous pouvez ensuite accéder à tout ce qui se trouve dans le module comme mymodule.myclass, etc.

gecco
la source
Une alternative consiste à imp.load_source(..)utiliser le impmodule comme suggéré dans cette réponse stackoverflow.com/questions/67631/…
Evgeni Sergeev
5
@gecco C'est pour import moduleNameoù moduleName est une chaîne. Et pourquoi pas from moduleName import *?
Nam G VU
6

a passé du temps à essayer d'importer des modules à partir d'une liste, et c'est le fil de discussion qui m'a guidé la plupart du temps - mais je n'ai pas compris l'utilisation de ___import____ -

alors voici comment importer un module à partir d'une chaîne et obtenir le même comportement que l'importation. Et essayez / sauf le cas d'erreur aussi. :)

  pipmodules = ['pycurl', 'ansible', 'bad_module_no_beer']
  for module in pipmodules:
      try:
          # because we want to import using a variable, do it this way
          module_obj = __import__(module)
          # create a global object containging our module
          globals()[module] = module_obj
      except ImportError:
          sys.stderr.write("ERROR: missing python module: " + module + "\n")
          sys.exit(1)

et oui, pour python 2.7> vous avez d'autres options - mais pour 2.6 <, cela fonctionne.

enthousiaste
la source
1

J'ai développé ces 3 fonctions utiles:

def loadModule(moduleName):
    module = None
    try:
        import sys
        del sys.modules[moduleName]
    except BaseException as err:
        pass
    try:
        import importlib
        module = importlib.import_module(moduleName)
    except BaseException as err:
        serr = str(err)
        print("Error to load the module '" + moduleName + "': " + serr)
    return module

def reloadModule(moduleName):
    module = loadModule(moduleName)
    moduleName, modulePath = str(module).replace("' from '", "||").replace("<module '", '').replace("'>", '').split("||")
    if (modulePath.endswith(".pyc")):
        import os
        os.remove(modulePath)
        module = loadModule(moduleName)
    return module

def getInstance(moduleName, param1, param2, param3):
    module = reloadModule(moduleName)
    instance = eval("module." + moduleName + "(param1, param2, param3)")
    return instance

Et chaque fois que je veux recharger une nouvelle instance, je dois juste appeler getInstance () comme ceci:

myInstance = getInstance("MyModule", myParam1, myParam2, myParam3)

Enfin, je peux appeler toutes les fonctions de la nouvelle instance:

myInstance.aFunction()

La seule spécificité ici est de personnaliser la liste des paramètres (param1, param2, param3) de votre instance.

prossblad
la source
1

En plus d'utiliser, importlibon peut également utiliserexec méthode pour importer un module à partir d'une variable chaîne.

Ici, je montre un exemple d'importation de la combinationsméthode à partir du itertoolspackage en utilisant la execméthode:

MODULES = [
    ['itertools','combinations'],
]

for ITEM in MODULES:
    import_str = "from {0} import {1}".format(ITEM[0],', '.join(str(i) for i in ITEM[1:]))
    exec(import_str)

ar = list(combinations([1, 2, 3, 4], 2))
for elements in ar:
    print(elements)

Production:

(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
Arsho
la source