Per aggiornare tutte le istanze di una classe, è necessario tenere traccia da qualche parte su tali istanze, in genere tramite riferimenti deboli (il valore debole è più maneggevole e generale), quindi la funzionalità "tenere traccia" non interrompe le istanze non necessarie dall'andare via, ovviamente!
Normalmente si desidera conservare un tale contenitore nell'oggetto di classe, ma, in questo caso, dal momento che si ricaricherà il modulo, ottenere il vecchio oggetto di classe non è banale; è più semplice lavorare a livello di modulo.
Quindi, diciamo che un "modulo aggiornabile" ha bisogno di definire, al suo inizio, un valore dict debole (e un ausiliario "tasto accanto a usare" int) con, diciamo, i nomi convenzionali:
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
Ogni classe nel modulo deve avere, in genere in __init__
, una chiamata _register(self)
per registrare nuove istanze.
Ora la "funzione di ricarica" può ottenere il roster di tutte le istanze di tutte le classi in questo modulo ottenendo una copia di _objs
prima di ricaricare il modulo.
Se tutto ciò che serve è quello di cambiare il codice, allora la vita è ragionevolmente facile:
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)
Ahimè, uno di solito non vuole dare la nuova classe la possibilità di aggiornare un oggetto della vecchia classe a se stesso più finemente, ad es con un metodo di classe adatto. Non è troppo difficile o:
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)
Per esempio, dicono che la nuova versione di classe Zap ha rinominato un attributo da foo a bar. Questo potrebbe essere il codice del nuovo 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
questo non è tutto - c'è molto di più da dire su questo argomento - Ma, è l'essenza, e la risposta è abbastanza lungo cammino già (ed io, abbastanza stanco ;-).
per rispondere alla tua subquestion "Perché questo è necessario?": E non è. import mymodule mette mymodule nello spazio dei nomi, ma mymodule è esso stesso un namespace, che contiene ClassChange, quindi devi affrontarlo con mymodule.ClassChange – balpha
Grazie balpha, mi sono chiesto per un po ':) – user129975
Tutte queste risposte sono ottimo cibo per il pensiero per me. Non so ancora quale risposta sia quella giusta, quindi farò un po 'di gioco con le soluzioni prima di selezionare. Grazie! :) – user129975