Wrapping a class set method

S

snoe

Hi there,

I have the following simplified classes:

class Project:
def __init__(self,pname):
self.devices = {} # Dictionary of Device objects
self.pname = pname

def setpname(self,pname):
self.pname = pname

def adddevice(self,dname):
self.devices[dname] = Device(self,dname)

class Device:
def __init__(self,parent,dname):
self.parent = parent
self.dname = dname

def setdname(self,dname):
self.dname = dname

Now, what I would like to do is wrap all of the set/add methods in a
function that pickles the Project object. I would then save the pickled
objects and use them to undo any changes to the above data structures.

I have a suspicion that there's an easier way to do this than
explicitly adding a Project.pickleme() call to the beginning of all of
my set/add methods.

So is there a way to wrap methods for this type of functionality or is
there another way of doing this, maybe without using setter methods?
 
B

Bengt Richter

Hi there,

I have the following simplified classes:

class Project:
def __init__(self,pname):
self.devices = {} # Dictionary of Device objects
self.pname = pname

def setpname(self,pname):
self.pname = pname

def adddevice(self,dname):
self.devices[dname] = Device(self,dname)

class Device:
def __init__(self,parent,dname):
self.parent = parent
self.dname = dname

def setdname(self,dname):
self.dname = dname

Now, what I would like to do is wrap all of the set/add methods in a
function that pickles the Project object. I would then save the pickled
objects and use them to undo any changes to the above data structures.

I have a suspicion that there's an easier way to do this than
explicitly adding a Project.pickleme() call to the beginning of all of
my set/add methods.

So is there a way to wrap methods for this type of functionality or is
there another way of doing this, maybe without using setter methods?
I would look into using properties with the same names as attributes of interest,
to store changes for possible undo/redo. For the devices attribute, you are not changing the
attribute per se, so you need a dict-like object that will do the state tracking when
you add devices. A dict subclass overriding __setitem__ should suffice for the usage you show.
Then you can give the classes undo/redo methods, or whatever. You could also use a special object
instead of a dict subclass, if you wanted some other interface.

Regards,
Bengt Richter
 
P

Paolino

snoe wrote:

I have a suspicion that there's an easier way to do this than
explicitly adding a Project.pickleme() call to the beginning of all of
my set/add methods.
So is there a way to wrap methods for this type of functionality or is
there another way of doing this, maybe without using setter methods?


I guess you are pointing to decorators, anyway you have to explicitly
wrap methods that are supposed to pickle.
Another way around is implement a metaclass and give the pickling
methods a special start name like set_ or add_ ,so having a protocol for
writing methods names.I paste the __metaclass__ solution

#### this is a skeleton

def saveStateWrapper(method,states):
from copy import copy
def wrapper(self,*_,**__):
self.__undoings.append(map(copy,[getattr(self,state) for state in
states])) # copy can not be idoneous
return method(self,*_,**__)
return wrapper

def initWrapper(init):
def wrapper(self,*_,**__):
self.__undoings=[]
init(self,*_,**__)
return wrapper

def undo(self): # an undoing method
if self.__undoings:
for state,was in zip(self.states,self.__undoings.pop(-1)):
setattr(self,state,was)

class Undoable(type): # the metaclass
def __init__(cls,name,bases,attrs):
cls.__init__=initWrapper(cls.__init__) # wrap init to add an
attribute __undoings to the instances
for attr in dir(cls):
if attr.split('_')[0] in ('add','set'): # look for attributes
protocolleds

setattr(cls,attr,saveStateWrapper(getattr(cls,attr),cls.states)) # wrap
methods
cls.undo=undo #add the undo method

class Project(object):
__metaclass__=Undoable
states=['pname','devices']

def __init__(self,pname):
self.devices = set()
self.pname = pname
def set_pname(self,pname):
self.pname = pname
def lookFor(self,dname): # names can change in the devices instances
for device in self.devices: # add exceptions checkings
if device.dname==dname:
return device
def add_device(self,dname):
self.devices.add(Device(self,dname))

class Device(object):
__metaclass__=Undoable
states=['dname']
def __init__(self,parent,dname):
self.parent = parent
self.dname = dname
def set_dname(self,dname):
self.dname = dname

project=Project('pippo')
project.set_pname('pupo')
assert project.pname=='pupo'
project.undo()
assert project.pname=='pippo'
project.add_device('aargh')
device=project.lookFor('aargh')
device.set_dname('sperem')
assert device==project.lookFor('sperem')
device.undo()
assert device==project.lookFor('aargh') ## :)
project.undo()






___________________________________
Yahoo! Mail: gratis 1GB per i messaggi e allegati da 10MB
http://mail.yahoo.it
 
P

Paolino

snoe wrote:

I have a suspicion that there's an easier way to do this than
explicitly adding a Project.pickleme() call to the beginning of all of
my set/add methods.
So is there a way to wrap methods for this type of functionality or is
there another way of doing this, maybe without using setter methods?


I guess you are pointing to decorators, anyway you have to explicitly
wrap methods that are supposed to pickle.
Another way around is implement a metaclass and give the pickling
methods a special start name like set_ or add_ ,so having a protocol for
writing methods names.I paste the __metaclass__ solution

#### this is a skeleton

def saveStateWrapper(method,states):
from copy import copy
def wrapper(self,*_,**__):
self.__undoings.append(map(copy,[getattr(self,state) for state in
states])) # copy can not be idoneous
return method(self,*_,**__)
return wrapper

def initWrapper(init):
def wrapper(self,*_,**__):
self.__undoings=[]
init(self,*_,**__)
return wrapper

def undo(self): # an undoing method
if self.__undoings:
for state,was in zip(self.states,self.__undoings.pop(-1)):
setattr(self,state,was)

class Undoable(type): # the metaclass
def __init__(cls,name,bases,attrs):
cls.__init__=initWrapper(cls.__init__) # wrap init to add an
attribute __undoings to the instances
for attr in dir(cls):
if attr.split('_')[0] in ('add','set'): # look for attributes
protocolleds

setattr(cls,attr,saveStateWrapper(getattr(cls,attr),cls.states)) # wrap
methods
cls.undo=undo #add the undo method

class Project(object):
__metaclass__=Undoable
states=['pname','devices']

def __init__(self,pname):
self.devices = set()
self.pname = pname
def set_pname(self,pname):
self.pname = pname
def lookFor(self,dname): # names can change in the devices instances
for device in self.devices: # add exceptions checkings
if device.dname==dname:
return device
def add_device(self,dname):
self.devices.add(Device(self,dname))

class Device(object):
__metaclass__=Undoable
states=['dname']
def __init__(self,parent,dname):
self.parent = parent
self.dname = dname
def set_dname(self,dname):
self.dname = dname

project=Project('pippo')
project.set_pname('pupo')
assert project.pname=='pupo'
project.undo()
assert project.pname=='pippo'
project.add_device('aargh')
device=project.lookFor('aargh')
device.set_dname('sperem')
assert device==project.lookFor('sperem')
device.undo()
assert device==project.lookFor('aargh') ## :)
project.undo()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,262
Messages
2,571,310
Members
47,977
Latest member
MillaDowdy

Latest Threads

Top