Historial de objetos de Python

Estoy tratando de encontrar una manera de hacer un seguimiento de varias reencarnaciones de un objeto en Python. Por ejemplo, me gustaría tener una clase para, por ejemplo, una sala. Esta sala podría comenzar su existencia como una “cocina”, y luego, en un momento dado, convertirse en un “estudio”. Quiero poder rastrear (o registrar) todas estas diversas fases en la vida de mi habitación. Entonces, se me ocurrió esto, y me pregunto si hay una mejor manera de hacerlo. O tal vez me esté perdiendo algo. Por lo que puedo decir, sin embargo, parece funcionar para mis propósitos.

class room(): def __init__(self,current="Any", history = []): self.current = childroom(current) self.history = history self.history.append(self.current) def changeroom(self,current = "New"): self.current = childroom(current) self.history.append(self.current) class childroom(room): def __init__(self,name="Any"): self.name = name 

Cuando uso este código …

 >>> myroom = room("kitchen") >>> myroom.changeroom("study") >>> myroom  >>> myroom.history [, ] >>> myroom.history[0].name 'kitchen' 

Yo personalmente lo implementaría así:

 #! /usr/bin/python3 import copy class Room: def __init__ (self, state = 'Initial'): self.state = state self.history = [] def morph (self, state): clone = copy.deepcopy (self) self.state = state self.history.append (clone) 

Tenga en cuenta que no sé si su configuración real tiene algunas características que restringen la copia profunda.

Esto produce:

 >>> r = Room ('Kitchen') >>> r.morph ('Loo') >>> r.morph ('Spaceship') >>> r.state 'Spaceship' >>> [a.state for a in r.history] ['Kitchen', 'Loo'] >>> [type (a) for a in r.history] [, ] 

Supongo que normalmente no es necesario guardar todo el estado de un objeto, sino solo los atributos que valen el seguimiento. Podría incluir este comportamiento en un decorador siguiendo estas líneas:

 #! /usr/bin/python3 import datetime import copy class Track: def __init__ (self, *args, saveState = False): self.attrs = args self.saveState = saveState def __call__ (self, cls): cls._track = [] this = self oGetter = cls.__getattribute__ def getter (self, attr): if attr == 'track': return self._track if attr == 'trackTrace': return '\n'.join ('{}: "{}" has changed to "{}"'.format (*t) for t in self._track) return oGetter (self, attr) cls.__getattribute__ = getter oSetter = cls.__setattr__ def setter (self, attr, value): if attr in this.attrs: self._track.append ( (datetime.datetime.now (), attr, copy.deepcopy (value) if this.saveState else value) ) return oSetter (self, attr, value) cls.__setattr__ = setter return cls 

Ahora podemos usar este decorador así:

 @Track ('holder') class Book: def __init__ (self, title): self.title = title self.holder = None self.price = 8 class Person: def __init__ (self, firstName, lastName): self.firstName = firstName self.lastName = lastName def __str__ (self): return '{} {}'.format (self.firstName, self.lastName) r = Book ('The Hitchhiker\'s Guide to the Galaxy') p = Person ('Pedro', 'Párramo') q = Person ('María', 'Del Carmen') r.holder = p r.price = 12 r.holder = q q.lastName = 'Del Carmen Orozco' print (r.trackTrace) 

Si se llama con @Track ('holder') , produce:

 2013-10-01 14:02:43.748855: "holder" has changed to "None" 2013-10-01 14:02:43.748930: "holder" has changed to "Pedro Párramo" 2013-10-01 14:02:43.748938: "holder" has changed to "María Del Carmen Orozco" 

Si se llama con @Track ('holder', 'price') , se obtiene:

 2013-10-01 14:05:59.433086: "holder" has changed to "None" 2013-10-01 14:05:59.433133: "price" has changed to "8" 2013-10-01 14:05:59.433142: "holder" has changed to "Pedro Párramo" 2013-10-01 14:05:59.433147: "price" has changed to "12" 2013-10-01 14:05:59.433151: "holder" has changed to "María Del Carmen Orozco" 

Si se llama con @Track ('holder', saveState = True) , produce:

 2013-10-01 14:06:36.815674: "holder" has changed to "None" 2013-10-01 14:06:36.815710: "holder" has changed to "Pedro Párramo" 2013-10-01 14:06:36.815779: "holder" has changed to "María Del Carmen" 

Esto parece un caso para un patrón de fachada: tenga un objeto de sala interior que capture el estado actual, y use __setattr__ en la fachada para capturar asignaciones, archivar el estado anterior (objeto interno) y crear uno nuevo con el valor actualizado de la propiedad. __getattr__ anular __getattr__ para delegar en el objeto interno, que también tendrá los métodos adecuados para cualquier otra cosa.