Para actualizar todas las instancias de una clase, es necesario realizar un seguimiento de esas instancias, normalmente mediante referencias débiles (el valor débil es la más práctica y general) por lo que la funcionalidad "mantener el seguimiento" no detendrá las instancias innecesarias de irse, por supuesto!
Normalmente, desea mantener un contenedor de este tipo en el objeto de la clase, pero, en este caso, dado que va a volver a cargar el módulo, obtener el objeto de la clase anterior no es trivial; es más sencillo trabajar a nivel de módulo.
Por lo tanto, digamos que un "módulo actualizable" tiene que definir, en su inicio, un valor dict débil (y un auxiliar "llave junto a utilizar" int) con, por ejemplo, nombres convencionales:
import weakref
class _List(list): pass # a weakly-referenceable sequence
_objs = weakref.WeakValueDictionary()
_nextkey = 0
def _register(obj):
_objs[_nextkey] = List((obj, type(obj).__name__))
_nextkey += 1
Cada clase del módulo debe tener, por lo general en __init__
, una llamada _register(self)
para registrar nuevas instancias.
Ahora la "función de recarga" puede obtener la lista de todas las instancias de todas las clases en este módulo obteniendo una copia de _objs
antes de volver a cargar el módulo.
Si todo lo que se necesita es cambiar el código, entonces la vida es razonablemente fácil:
def reload_all(amodule):
objs = getattr(amodule, '_objs', None)
reload(amodule)
if not objs: return # not an upgraable-module, or no objects
newobjs = getattr(amodule, '_objs', None)
for obj, classname in objs.values():
newclass = getattr(amodule, classname)
obj.__class__ = newclass
if newobjs: newobjs._register(obj)
Por desgracia, uno normalmente no quieren dar la nueva clase la oportunidad de actualizar un objeto de la clase de edad a sí mismo más finamente, por ejemplo por un método de clase adecuado. Eso no es demasiado duro, ya sea:
def reload_all(amodule):
objs = getattr(amodule, '_objs', None)
reload(amodule)
if not objs: return # not an upgraable-module, or no objects
newobjs = getattr(amodule, '_objs', None)
for obj, classname in objs:
newclass = getattr(amodule, classname)
upgrade = getattr(newclass, '_upgrade', None)
if upgrade:
upgrade(obj)
else:
obj.__class__ = newclass
if newobjs: newobjs._register(obj)
Por ejemplo, dicen que la nueva versión de la clase Zap ha cambiado el nombre de un atributo de foo a bar. Este podría ser el código del nuevo Zap:
class Zap(object):
def __init__(self):
_register(self)
self.bar = 23
@classmethod
def _upgrade(cls, obj):
obj.bar = obj.foo
del obj.foo
obj.__class__ = cls
esto no es todo - hay mucho más que decir sobre el tema - Pero, es la esencia, y la respuesta es mucho el tiempo suficiente ya (y yo, agotado lo suficiente ;-).
para responder a sus preguntas adicionales indicadas "¿Por qué es necesario?": No se trata. import mymodule pone mymodule al espacio de nombres, pero mymodule es en sí mismo un espacio de nombres, que contiene ClassChange, por lo que tiene que abordarlo con mymodule.ClassChange – balpha
Gracias balpha, me he estado preguntando por un tiempo :) – user129975
Todas estas respuestas son una gran comida para pensar para mí. Todavía no sé qué respuesta es la correcta, así que jugaré un poco con las soluciones antes de seleccionar. ¡Gracias! :) – user129975