kamil kisiel
Estoy escribiendo una aplicación de Python que toma un comando como argumento, por ejemplo:
$ python myapp.py command1
Quiero que la aplicación sea extensible, es decir, poder agregar nuevos módulos que implementen nuevos comandos sin tener que cambiar la fuente principal de la aplicación. El árbol se parece a:
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 un __import__()
función, que toma una cadena para un nombre de módulo:
__import__(name, globals=None, locals=None, fromlist=(), level=0)
La función importa el módulo.
name
potencialmente usando el dadoglobals
ylocals
para determinar cómo interpretar el nombre en un contexto de paquete. losfromlist
da los nombres de los objetos o submódulos que deben importarse del módulo dado porname
.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, solo me pregunto si posiblemente haya 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 en el uso de huevos o puntos de extensión. Este no es un proyecto de código abierto y no espero que haya “complementos”. El objetivo es simplificar el código de la aplicación principal y eliminar la necesidad de modificarlo cada vez que se agrega un nuevo módulo de comando.
Ver también: ¿Cómo importo un módulo dada la ruta completa?
harley holcombe
Con Python anterior a 2.7/3.1, así es como se hace.
Para versiones más recientes, consulte importlib.import_module
por Pitón 2 y Pitón 3.
Puedes usar exec
si tu tambien quieres.
o usando __import__
puede 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 Sumérgete en Python.
-
¿Cuál es la diferencia con exec?
– usuario1767754
17 de septiembre de 2014 a las 7:10
-
Un problema con esta solución para el OP es que atrapar excepciones para uno o dos módulos de “comando” defectuosos hace que toda su aplicación de comando falle en una excepción. Personalmente, haría un bucle sobre cada importar envuelto individualmente en un intento: mods=__import__()\nexcepto ImportError como error: informe (error) para permitir que otros comandos continúen funcionando mientras se arreglan los malos.
– DevPlayer
08/04/2015 a las 13:38
-
Otro problema con esta solución, como Denis Malinovsky señala a continuación, es que los propios documentos de Python recomiendan no usar
__import__
. Los documentos 2.7: “Debido a que esta función está diseñada para que la use el intérprete de Python y no para uso general, es mejor usar importlib.import_module ()…” Al usar python3, elimp
El módulo resuelve este problema, como menciona monkut a continuación.– LiavK
8 de julio de 2015 a las 15:14
-
@JohnWu ¿por qué necesitas
foreach
cuando tengasfor element in
ymap()
aunque 🙂– cowbert
19 de febrero de 2018 a las 22:59
-
Tenga en cuenta que el mapa NO funcionará en python 3, ya que el mapa ahora usa una evaluación diferida.
– qué
13 de noviembre de 2018 a las 21:52
Denis Malinovsky
los recomendado forma para Python 2.7 y 3.1 y posteriores es usar importlib
módulo:
importlib.import_module(
name, package=None)
Importar un módulo. El argumento del nombre especifica qué módulo 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 en el nombre del paquete que actuará como ancla para resolver el nombre del paquete (p. ej.import_module('..mod', 'pkg.subpkg')
importarápkg.mod
).
p.ej
my_module = importlib.import_module('os.path')
-
¿Recomendado por qué fuente o autoridad?
– michuelnik
08/04/2015 a las 13:21
-
Consejos de documentación contra usando
__import__
función a favor del módulo mencionado anteriormente.– Denis Malinovsky
9 de abril de 2015 a las 12:42
-
Esto funciona bien para la importación.
os.path
; qué tal sifrom os.path import *
?– Nam G VU
12 de junio de 2017 a las 6:31
-
Obtengo la respuesta aquí stackoverflow.com/a/44492879/248616 es decir. vocación
globals().update(my_module.__dict)
– Nam G VU
12 de junio de 2017 a las 6:46
-
@NamGVU, este es un método peligroso ya que está contaminando sus globales y puede anular cualquier identificador con los mismos nombres. El enlace que ha publicado tiene una versión mejor y mejorada de este código.
– Denis Malinovsky
19 de junio de 2017 a las 17:11
monje
Nota: diablillo está en desuso desde Python 3.4 a favor de importlib
Como se mencionó el diablillo El módulo 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 los métodos definidos que se requerían. 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
-
Buena y sencilla solución. Escribí uno similar: stamat.wordpress.com/dynamic-module-import-in-python Pero el tuyo tiene algunos defectos: ¿Qué pasa con las excepciones? IOError y ImportError? ¿Por qué no buscar primero la versión compilada y luego la versión fuente? Esperar una clase reduce la reutilización en su caso.
– stamat
30/06/2013 a las 20:40
-
En la línea donde construye MyClass en el módulo de destino, está agregando una referencia redundante al nombre de la clase. ya esta guardado en
expected_class
para que puedas hacerclass_inst = getattr(py_mod,expected_name)()
en cambio.– Andrés
16 de octubre de 2013 a las 7:11
-
Tenga en cuenta que si existe un archivo compilado de bytes que coincida correctamente (con el sufijo .pyc o .pyo), se usará en lugar de analizar el archivo fuente dado.. https://docs.python.org/2/library/imp.html#imp.load_source
– cdosborn
30 de junio de 2015 a las 21:07
-
Atención: esta solución funciona; sin embargo, el módulo imp quedará obsoleto a favor de import lib, consulte la página de imp: “””Obsoleto desde la versión 3.4: el paquete imp está pendiente de obsolescencia a favor de importlib.”””
– nsx
20 de noviembre de 2016 a las 9:50
Brandt
Hoy en día deberías usar importlib.
Importar un archivo fuente
los documentos en realidad proporciona 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)
De esta manera puede acceder a los miembros (por ejemplo, una función “hello
“) de su módulo pluginX.py
— en este fragmento llamado module
— bajo su espacio de nombres; P.ej, module.hello()
.
Si desea importar los miembros (p. ej., “hello
“) puede incluir module
/pluginX
en la lista de módulos en memoria:
sys.modules[module_name] = module
from pluginX import hello
hello()
Importar un paquete
Importación de un paquete (p.ej, pluginX/__init__.py
) bajo su directorio actual es realmente sencillo:
import importlib
pkg = importlib.import_module('pluginX')
# check if it's all there..
def bla(mod):
print(dir(mod))
bla(pkg)
Utilizar el módulo diablilloo el más directo __import__()
función.
-
No use __import__ ver Documento de Python utilizar una importlib.import_module
– fcm
28 de junio de 2019 a las 15:01
jonathan livni
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()
-
No use __import__ ver Documento de Python utilizar una importlib.import_module
– fcm
28 de junio de 2019 a las 15:01
georgia
Puedes usar exec
:
exec("import myapp.commands.%s" % command)
-
¿Cómo obtengo un identificador del módulo a través del exec, de modo que pueda llamar (en este ejemplo) al método .run()?
– Kamil Kisiel
19 de noviembre de 2008 a las 6:19
-
Luego puede hacer getattr(myapp.commands, command) para acceder al módulo.
– Greg Hewgill
19 de noviembre de 2008 a las 6:20
-
… o añadir
as command_module
hasta el final de la declaración de importación y luego hacercommand_module.run()
– buey
16/10/2013 a las 10:43
-
En alguna versión de Python 3+ ejecutivo se convirtió en una función con el beneficio adicional de que la referencia resultante creada en la fuente se almacena en el argumento locals() de exec(). Para aislar aún más el ejecutivo al que se hace referencia de los locales del bloque de código local, puede proporcionar su propio dictado, como uno vacío y hacer referencia a las referencias usando ese dictado, o pasar ese dictado a otras funciones exec (fuente, gobals(), command1_dict) …. imprimir (comando1_dict[‘somevarible’])
– DevPlayer
08/04/2015 a las 13:43
¿Qué significa fromlist=[“myapp.commands”] ¿hacer?
– Pieter Muller
6 de junio de 2012 a las 10:29
@PieterMüller: en un shell de Python, escriba esto:
dir(__import__)
. La fromlist debe ser una lista de nombres para emular “from name import…”.– mawimawi
9 de agosto de 2012 a las 7:13
Importar módulo desde variable de cadena
– tormenta_m2138
12 de agosto de 2015 a las 18:28
A partir de 2019, debe buscar
importlib
: stackoverflow.com/a/54956419/687896– Brandt
13 de marzo de 2019 a las 9:42
No use __import__ ver Documento de Python utilizar una importlib.import_module
– fcm
28 junio 2019 a las 15:00