Módulo de recarga que ha sido importado a otro módulo.

Seamos realistas, todo el negocio de recargar el código de Python después de cambiarlo es un desastre. Descubrí hace un tiempo que llamar a import en el intérprete es mejor que from import , porque luego puedo llamar a reload(module) para obtener el código actualizado.

Pero ahora tengo problemas más complicados. Así que tengo este archivo, module1.py, y en la parte superior dice:

 from module2 import , , etc. 

Y luego voy y cambio el código dentro de module2. Resulta que la reload(module1) llamada reload(module1) no volverá a cargar el código cambiado en el módulo2, aunque el código del módulo2 se importe en la parte superior del módulo1. ¿Hay alguna forma de recargar todo sin reiniciar el intérprete?

Antes de que alguien se meta en mi caso sobre el estilo, solo diré:

  1. Solo llamo a reload desde el intérprete, nunca en código activo. Esta pregunta se refiere cuando estoy probando un nuevo código.
  2. Nunca llamo desde import * , sé que destruye la legibilidad

    Echa un vistazo a IPython. Tiene la extensión de carga automática que recarga automáticamente los módulos durante la sesión del intérprete antes de llamar a las funciones internas . Cito el ejemplo de la página de destino:

     In [1]: %load_ext autoreload In [2]: %autoreload 2 In [3]: from foo import some_function In [4]: some_function() Out[4]: 42 In [5]: # open foo.py in an editor and change some_function to return 43 In [6]: some_function() Out[6]: 43 

    Para recargar un módulo, debe usar la reload , y debe usarlo en el módulo que desea recargar. La recarga de un módulo no recarga de forma recursiva todos los módulos importados por ese módulo. Simplemente vuelve a cargar ese módulo.

    Cuando se importa un módulo, se almacena una referencia a él, y las importaciones posteriores de ese módulo reutilizan la versión ya importada, ya almacenada. Cuando module1 cargar el module1 , vuelve a ejecutar la from module2 import ... , pero eso solo reutiliza la versión ya importada del module2 sin volver a cargarla.

    La única forma de solucionarlo es cambiar el código para que import module2 lugar de (o además de) el from module2 import ... No puede volver a cargar un módulo a menos que el módulo en sí haya sido importado y vinculado a un nombre (es decir, con una statement del import module , no solo una statement from module import stuff ).

    Tenga en cuenta que puede usar ambas formas de importación, y volver a cargar el módulo importado afectará posteriormente from importaciones. Es decir, puedes hacer esto:

     >>> import module >>> from module import x >>> x 2 # Change module code here, changing x to 3 >>> reload(module) >>> from module import x >>> x 3 

    Esto puede ser útil para el trabajo interactivo, ya que le permite usar nombres cortos y sin prefijo para referirse a lo que necesita, mientras aún puede recargar el módulo.

    En lugar de mejorar en la recarga de módulos, podría mejorar en el reinicio del intérprete. Por ejemplo, puedes poner tu código de configuración en su propio archivo y luego ejecutarlo de esta manera:

     $ python -i setup.py >>> 

    Esto ejecutará setup.py, luego lo dejará en el indicador interactivo. O, en lugar de hacer mucho trabajo en el indicador interactivo, escriba pruebas automatizadas que hagan su trabajo por usted.

    Tienes razón, recargar módulos en Python es un desastre. La semántica del lenguaje hace que sea difícil cambiar el código mientras se está ejecutando el proceso. Aprende a no necesitar módulos de recarga, estarás más contento.

    Ok, no estoy seguro de que califique como una respuesta sin un cambio en el código, pero … al menos, eso no implica un cambio en el module1 .

    Puede usar alguna clase de envoltura de módulo, que guarda los módulos cargados antes y después de cargar el módulo 1 y proporciona un método de reload , algo así:

     import sys class Reloader(object): def __init__(self, modulename): before = sys.modules.keys() __import__(modulename) after = sys.modules.keys() names = list(set(after) - set(before)) self._toreload = [sys.modules[name] for name in names] def do_reload(self): for i in self._toreload: reload(i) 

    Luego carga el módulo 1 con:

     reloader = Reloader('module1') 

    Luego, puede modificar el módulo 2 y volver a cargarlo en el intérprete con:

     reloader.do_reload() 

    No olvide que una importación es realmente simplemente asignar un nombre en un espacio de nombres. Entonces, puedes reasignar ese nombre después de recargar:

     >>> reload(module2) >>> module1.class1 = module2.class1 

    Ahora el objeto class1 dentro de module1 se refiere a la versión recargada de module2.

    Aquí hay una función de recarga recursiva que puedes usar (crédito para @Matthew): https://stackoverflow.com/a/17194836/1020625