¿Cómo importar un módulo dado su nombre?

Estoy escribiendo una aplicación de Python que toma como comando como argumento, por ejemplo:

$ python myapp.py command1 

Quiero que la aplicación sea extensible, es decir, que pueda agregar nuevos módulos que implementen nuevos comandos sin tener que cambiar la fuente principal de la aplicación. El árbol se ve algo así como:

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

Así que quiero que la aplicación encuentre los módulos de comando disponibles en tiempo de ejecución y ejecute el apropiado.

Python define una función __import__ , que toma una cadena para un nombre de módulo:

__import __ (nombre, globales = Ninguno, locales = Ninguno, fromlist = (), nivel = 0)

La función importa el nombre del módulo, utilizando potencialmente los globales y locales dados para determinar cómo interpretar el nombre en el contexto de un paquete. La lista de origen proporciona los nombres de los objetos o submódulos que deben importarse del módulo dado por nombre.

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

Así que actualmente tengo algo como:

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

Esto funciona bien, me pregunto si existe posiblemente una forma más idiomática de lograr lo que estamos haciendo con este código.

Tenga en cuenta que específicamente no quiero entrar al uso de huevos o puntos de extensión. Este no es un proyecto de código abierto y no espero que haya “complementos”. El punto es simplificar el código principal de la aplicación y eliminar la necesidad de modificarlo cada vez que se agregue un nuevo módulo de comando.

Con Python anterior a 2.7 / 3.1, así es como lo haces.

Para versiones más nuevas, vea importlib.import_module para Python 2 y Python 3 .

Puedes usar exec si quieres también.

O usando __import__ puedes importar una lista de módulos haciendo esto:

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

Arrancado directamente de Dive Into Python .

La forma recomendada para Python 2.7 y 3.1 y posteriores es usar el módulo importlib :

importlib.import_module (nombre, paquete = Ninguno)

Importar un módulo. El argumento de nombre especifica qué módulo se va a importar en términos absolutos o relativos (por ejemplo, pkg.mod o ..mod). Si el nombre se especifica en términos relativos, entonces el argumento del paquete debe establecerse al nombre del paquete que debe actuar como el ancla para resolver el nombre del paquete (por ejemplo, import_module (‘.. mod’, ‘pkg.subpkg’) importará pkg.mod).

p.ej

 my_module = importlib.import_module('os.path') 

Nota: imp está en desuso desde Python 3.4 a favor de importlib

Como se mencionó anteriormente, el módulo imp le proporciona funciones de carga:

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

He usado estos antes para realizar algo similar.

En mi caso, definí una clase específica con métodos definidos que eran necesarios. Una vez que cargué el módulo, verificaría si la clase estaba en el módulo y luego crearía una instancia de esa clase, algo como esto:

 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 

Use el módulo imp , o la función más directa __import__() .

Si lo quieres en tus locales:

 >>> 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)]' 

Lo mismo funcionaría con globals()

Puedes usar exec :

 exec "import myapp.commands.%s" % command 

Similar a la solución de @monkut pero reutilizable y tolerante a errores, se describe aquí 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 

Hoy en día deberías usar importlib .

Importar un archivo fuente

Los documentos realmente proporcionan una receta para eso, y dice así:

 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) 

Importar un paquete

Importar un paquete ( por ejemplo , pluginX/__init__.py ) en su directorio actual es realmente sencillo:

 import importlib pluginX = importlib.import_module('pluginX') # check if it's all there.. def bla(mod): print(dir(mod)) bla(module) 

Parece que lo que realmente quieres es una architecture de plugin.

Debería echar un vistazo a la funcionalidad de puntos de entrada que proporciona el paquete setuptools. Ofrece una excelente manera de descubrir complementos que se cargan para su aplicación.

por ejemplo: los nombres de mis módulos son como jan_module / feb_module / mar_module

mes = ‘feb’
exec ‘de% s_module import *’% (month)

La siguiente pieza funcionó para mí:

 >>>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 quieres importar en shell-script:

 python -c '' 

Lo siguiente me funcionó:

 import sys, glob sys.path.append('/home/marc/python/importtest/modus') fl = glob.glob('modus/*.py') modulist = [] adapters=[] for i in range(len(fl)): fl[i] = fl[i].split('/')[1] fl[i] = fl[i][0:(len(fl[i])-3)] modulist.append(getattr(__import__(fl[i]),fl[i])) adapters.append(modulist[i]()) 

Carga módulos desde la carpeta ‘modus’. Los módulos tienen una sola clase con el mismo nombre que el nombre del módulo. Por ejemplo, el archivo modus / modu1.py contiene:

 class modu1(): def __init__(self): self.x=1 print self.x 

El resultado es una lista de clases “adaptadores” cargadas dinámicamente.