notebook modules from create python import ipython python-module python-import

from - python modules list



Evite que Python guarde en caché los módulos importados (6)

Mientras desarrollaba un proyecto extenso (dividido en varios archivos y carpetas) en Python con IPython, me encontré con el problema de los módulos importados en caché.

El problema es que el import module instrucciones solo lee el módulo una vez, incluso si ese módulo ha cambiado. Entonces, cada vez que cambio algo en mi paquete, debo cerrar y reiniciar IPython. Doloroso.

¿Hay alguna manera de forzar adecuadamente la recarga de algunos módulos? O, mejor aún, para evitar que Python los guarde en caché.

Intenté varios enfoques, pero ninguno funciona. En particular, me encuentro con errores realmente extraños, como algunos módulos o variables que se vuelven misteriosamente iguales a None ...

El único recurso sensato que encontré es Recargar módulos de Python , desde pyunit, pero no lo he comprobado. Me gustaría algo así.

Una buena alternativa sería que IPython se reinicie o reinicie el intérprete de Python de alguna manera.

Entonces, si desarrolla en Python, ¿qué solución ha encontrado para este problema?

Editar

Para aclarar las cosas: obviamente, entiendo que algunas variables antiguas que dependen del estado anterior del módulo pueden quedarse. Eso está bien para mi. ¿Por qué es tan difícil en Python forzar la recarga de un módulo sin que suceda todo tipo de errores extraños?

Más específicamente, si tengo mi módulo completo en un archivo module.py entonces lo siguiente funciona bien:

import sys try: del sys.modules[''module''] except AttributeError: pass import module obj = module.my_class()

Esta pieza de código funciona muy bien y puedo desarrollarme sin salir de IPython durante meses.

Sin embargo , cada vez que mi módulo está compuesto por varios submódulos, el infierno se desata:

import os for mod in [''module.submod1'', ''module.submod2'']: try: del sys.module[mod] except AttributeError: pass # sometimes this works, sometimes not. WHY?

¿Por qué es tan diferente para Python si tengo mi módulo en un gran archivo o en varios submódulos? ¿Por qué ese enfoque no funcionaría?


Aquí ya hay algunas respuestas realmente buenas, pero vale la pena saber acerca de dreload, que es una función disponible en IPython que funciona como "deep reload". De la documentación:

El módulo IPython.lib.deepreload le permite volver a cargar recursivamente un módulo: los cambios realizados en cualquiera de sus dependencias se volverán a cargar sin tener que salir. Para empezar a usarlo, hazlo:

http://ipython.org/ipython-doc/dev/interactive/reference.html#dreload

Está disponible como un cuaderno "global" en IPython (al menos mi versión, que se ejecuta v2.0).

HTH


Con IPython viene la extensión de autorecarga que repite automáticamente una importación antes de cada llamada de función. Funciona al menos en casos simples, pero no confíe demasiado en él: en mi experiencia, un reinicio de intérprete se requiere de vez en cuando, especialmente cuando los cambios de código ocurren solo en el código importado indirectamente.

Ejemplo de uso de la página vinculada:

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


Dejar de fumar y reiniciar el intérprete es la mejor solución. Cualquier tipo de estrategia de recarga en vivo o sin almacenamiento en caché no funcionará sin problemas porque los objetos de módulos que ya no existen pueden existir porque los módulos a veces almacenan el estado y porque incluso si su caso de uso realmente permite la recarga en caliente es demasiado complicado pensar en valer la pena.


Estoy usando PythonNet en mi proyecto. Afortunadamente, encontré que hay un comando que puede resolver perfectamente este problema.

using (Py.GIL()) { dynamic mod = Py.Import(this.moduleName); if (mod == null) throw new Exception( string.Format("Cannot find module {0}. Python script may not be complied successfully or module name is illegal.", this.moduleName)); // This command works perfect for me! PythonEngine.ReloadModule(mod); dynamic instance = mod.ClassName();


Puede usar la maquinaria de enlace de importación que se describe en PEP 302 para cargar no módulos sino un tipo de objeto proxy que le permitirá hacer lo que quiera con el objeto de módulo subyacente: volver a cargarlo, soltar referencia a él, etc.

El beneficio adicional es que su código actual no requerirá cambios y esta funcionalidad adicional del módulo puede ser arrancada de un único punto en el código, donde realmente agrega el buscador a sys.meta_path .

Algunas ideas sobre la implementación: cree un buscador que acepte encontrar cualquier módulo, excepto el incorporado (no tiene nada que ver con módulos integrados), luego cree el cargador que devolverá el objeto proxy subclasificado de types.ModuleType lugar de un objeto de módulo real. Tenga en cuenta que el objeto cargador no está obligado a crear referencias explícitas a los módulos cargados en sys.modules , pero se recomienda encarecidamente, ya que, como ya ha visto, puede fallar de forma inexplicable. El objeto proxy debe capturar y reenviar todos los __getattr__ , __setattr__ y __delattr__ al módulo real subyacente al que se refiere. Probablemente no necesite definir __getattribute__ porque no escondería el contenido real del módulo con sus métodos de proxy. Entonces, ahora debe comunicarse con el proxy de alguna manera: puede crear algún método especial para soltar la referencia subyacente, luego importar el módulo, extraer la referencia del proxy devuelto, soltar el proxy y mantener la referencia al módulo recargado. Uf, parece aterrador, pero debería arreglar tu problema sin volver a cargar Python cada vez.


verifique si el módulo está en sys.modules , y si lo está, lo devuelve. Si desea importar para cargar el módulo desde el disco, primero puede eliminar la clave apropiada en sys.modules .

Existe la función de reload incorporada que, dado un objeto de módulo, volverá a cargar desde el disco y que se colocará en sys.modules . Editar : en realidad, recompilará el código del archivo en el disco y luego volverá a evaluarlo en el __dict__ del módulo existente. Algo potencialmente muy diferente a hacer un nuevo objeto de módulo.

Mike Graham tiene razón; volver a cargar correctamente si tiene incluso algunos objetos en vivo que hacen referencia al contenido del módulo que ya no quiere es difícil. Los objetos existentes seguirán haciendo referencia a las clases desde las que se crearon las instancias, pero también todas las referencias creadas mediante el from module import symbol seguirán apuntando a cualquier objeto de la versión anterior del módulo. Muchas cosas sutilmente incorrectas son posibles.

Editar: Estoy de acuerdo con el consenso de que reiniciar el intérprete es, con mucho, la cosa más confiable. Pero para fines de depuración, supongo que podría intentar algo como lo siguiente. Estoy seguro de que hay casos de esquina para los que esto no funcionaría, pero si no está haciendo nada demasiado loco (de lo contrario) con la carga del módulo en su paquete, podría ser útil.

def reload_package(root_module): package_name = root_module.__name__ # get a reference to each loaded module loaded_package_modules = dict([ (key, value) for key, value in sys.modules.items() if key.startswith(package_name) and isinstance(value, types.ModuleType)]) # delete references to these loaded modules from sys.modules for key in loaded_package_modules: del sys.modules[key] # load each of the modules again; # make old modules share state with new modules for key in loaded_package_modules: print ''loading %s'' % key newmodule = __import__(key) oldmodule = loaded_package_modules[key] oldmodule.__dict__.clear() oldmodule.__dict__.update(newmodule.__dict__)

Lo que muy brevemente probé como tal:

import email, email.mime, email.mime.application reload_package(email)

impresión:

reloading email.iterators reloading email.mime reloading email.quoprimime reloading email.encoders reloading email.errors reloading email reloading email.charset reloading email.mime.application reloading email._parseaddr reloading email.utils reloading email.mime.base reloading email.message reloading email.mime.nonmultipart reloading email.base64mime