web-dev-qa-db-fra.com

Comment importer un module étant donné son nom?

J'écris une application Python qui prend comme commande un argument, par exemple:

$ python myapp.py command1

Je veux que l'application soit extensible, c'est-à-dire pouvoir ajouter de nouveaux modules qui implémentent de nouvelles commandes sans avoir à changer la source principale de l'application. L'arbre ressemble à quelque chose comme:

myapp/
    __init__.py
    commands/
        __init__.py
        command1.py
        command2.py
    foo.py
    bar.py

Je souhaite donc que l'application trouve les modules de commande disponibles au moment de l'exécution et exécute celui qui convient.

Python définit une fonction _IMPORT_, qui prend une chaîne pour un nom de module:

__import __ (name, globals = None, locals = None, fromlist = (), level = 0)

La fonction importe le nom du module, en utilisant éventuellement les variables globales et locales indiquées pour déterminer comment interpréter le nom dans un contexte de package. La liste from donne les noms des objets ou des sous-modules à importer du module donné par nom. 

Source: https://docs.python.org/3/library/functions.html#import

Donc actuellement j'ai quelque chose comme:

command = sys.argv[1]
try:
    command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
    # Display error message

command_module.run()

Cela fonctionne très bien, je me demande s'il existe peut-être un moyen plus idiomatique d'accomplir ce que nous faisons avec ce code.

Notez que je ne veux surtout pas utiliser d’œufs ou de points d’extension. Ce n'est pas un projet open-source et je ne m'attends pas à ce qu'il y ait des "plugins". Le but est de simplifier le code principal de l'application et de supprimer le besoin de le modifier chaque fois qu'un nouveau module de commande est ajouté.

414
Kamil Kisiel

Avec Python plus ancien que 2.7/3.1, c'est à peu près ce que vous faites. 

Pour les versions plus récentes, voir importlib.import_module pour Python 2 et Python 3 .

Vous pouvez aussi utiliser exec si vous le souhaitez.

Ou en utilisant __import__, vous pouvez importer une liste de modules en procédant comme suit:

>>> moduleNames = ['sys', 'os', 're', 'unittest'] 
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)

Déchiré directement de Plonger dans Python .

258
Harley Holcombe

La méthode recommandée pour Python 2.7 et 3.1 et versions ultérieures consiste à utiliser importlib module:

importlib.import_module (name, package = None)

Importer un module. L'argument name spécifie le module à importer en termes absolus ou relatifs (par exemple, pkg.mod ou ..mod). Si le nom est spécifié en termes relatifs, l'argument de package doit être défini sur le nom du package qui doit servir d'ancre pour la résolution du nom du package (par exemple, import_module ('.. mod', 'pkg.subpkg') importera pkg.mod).

par exemple.

my_module = importlib.import_module('os.path')
247
Denis Malinovsky

Remarque: imp est obsolète depuis Python 3.4 au profit de importlib.

Comme mentionné précédemment, le module imp vous fournit les fonctions de chargement:

imp.load_source(name, path)
imp.load_compiled(name, path)

J'ai déjà utilisé ceux-ci pour effectuer quelque chose de similaire. 

Dans mon cas, j'ai défini une classe spécifique avec les méthodes définies qui étaient requises . Une fois que j'ai chargé le module, je vérifiais si la classe était dans le module, puis créez une instance de cette classe.

import imp
import os

def load_from_file(filepath):
    class_inst = None
    expected_class = 'MyClass'

    mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1])

    if file_ext.lower() == '.py':
        py_mod = imp.load_source(mod_name, filepath)

    Elif file_ext.lower() == '.pyc':
        py_mod = imp.load_compiled(mod_name, filepath)

    if hasattr(py_mod, expected_class):
        class_inst = getattr(py_mod, expected_class)()

    return class_inst
126
monkut

Utilisez le module imp ou la fonction plus directe __import__() .

19
gimel

Si vous le souhaitez dans vos locaux:

>>> mod = 'sys'
>>> locals()['my_module'] = __import__(mod)
>>> my_module.version
'2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]'

idem fonctionnerait avec globals()

13
Jonathan

Vous pouvez utiliser exec :

exec "import myapp.commands.%s" % command
9
Greg Hewgill

Semblable à la solution @monkut mais réutilisable et tolérant aux erreurs, décrit ici http://stamat.wordpress.com/dynamic-module-import-in-python/ :

import os
import imp

def importFromURI(uri, absl):
    mod = None
    if not absl:
        uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri))
    path, fname = os.path.split(uri)
    mname, ext = os.path.splitext(fname)

    if os.path.exists(os.path.join(path,mname)+'.pyc'):
        try:
            return imp.load_compiled(mname, uri)
        except:
            pass
    if os.path.exists(os.path.join(path,mname)+'.py'):
        try:
            return imp.load_source(mname, uri)
        except:
            pass

    return mod
3
stamat

De nos jours, vous devriez utiliser importlib .

Importer un fichier source

Les docs fournissent en fait une recette pour cela, et cela ressemble à:

import sys
import importlib.util

file_path = 'pluginX.py'
module_name = 'pluginX'

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(module)

Importer un package

Importer un paquet ( par exemple , pluginX/__init__.py) sous votre répertoire actuel est simple:

import importlib

pluginX = importlib.import_module('pluginX')

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(module)
3
Brandt

par exemple: mes noms de modules sont comme jan_module/feb_module/mar_module

mois = 'février'
exec 'from% s_module import *'% (month)

1
chiru

Cela ressemble à ce que vous voulez vraiment, c'est une architecture de plugin. 

Vous devriez jeter un coup d'œil à la fonctionnalité entry points fournie par le paquet setuptools. Il offre un excellent moyen de découvrir les plug-ins chargés pour votre application.

1
dowski

La pièce ci-dessous a fonctionné pour moi:

>>>import imp; 
>>>fp, pathname, description = imp.find_module("/home/test_module"); 
>>>test_module = imp.load_module("test_module", fp, pathname, description);
>>>print test_module.print_hello();

si vous voulez importer dans Shell-script:

python -c '<above entire code in one line>'
0
PanwarS87