I’m trying to improve (if possible) upon the DataContext class below or find alternate solutions:
class Store(object):
def __init__( self, contents=None):
self.contents = contents
class DataContext(object):
def __init__( self, datastore ) : # datastore is of type store
self.store = datastore
def __enter__( self ) :
self.temp_store = copy.copy( self.store.contents ) # Improve upon this!
def __exit__( self, type, value, traceback ) :
self.store.contents = self.temp_store
Example usage:
data = Store( [1,2,3] )
print "Before context: ", data.contents
with DataContext( data ):
data.contents.append( 4 ) # Tampering with the data
print "Within context: ", data.contents
print "Outside context: ", data.contents
Output:
Before context: [1, 2, 3]
Within context: [1, 2, 3, 4]
Outside context: [1, 2, 3]
The copy can be expensive for large data structures. What would be (or is there) a clean way to only store changes to the data structure inside the context and then undo those specific changes to data during exit?
You could use some sort of checkpointing. For instance, you could keep a separate structure where you would insert new elements. When you exited the context, you would need to remove these new elements from the container.
Clearly, whether this is worth compared to doing a deep cooping will depend on several factors such as:
Moreover, in terms of correctness the type of container also affects the design of such a mechanism. For instance, if the container is a list you would need to remove all the elements that you inserted while in the context. On the other hand, if the container is a set, you would only need to remove them if they were not initially present.
For simplicity, let’s assume you are only interested in lists and just supporting
appendoperation, then a possible solution is actually very easy:class DataContext(object):
Definitely this is a very simple case, and just for adding support for removing elements from anywhere in the list, you will need to keep some sort of log of the operations performed on the list. The log could be a list of entries specifying the type of operation (insertion, deletion) and its arguments (e.g., index, data). Moreover, you will need to use a proxy or wrapper to intercept every modifying operation on the list (e.g.,
append,extend,insert,remove, etc.) if you want to give support for all the list operations.If you want to go even more generic and support not only lists but other types of containers, then you would need wrappers for the operations of the different containers. For instance, for
setyou would need to supportadd,remove,update,intersection_update, etc. To do that you could create a class hierarchy descending fromDataContextwith the specialized operations for each type.Anyway, as you can see, the complexity to implement this grows significantly depending on how much generic you want to be. So, probably if you just want a specific solution tailored for lists, it may pay off to implement it, otherwise it may just be better to pay the price of performing a copy of the container.