By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,222 Members | 1,436 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,222 IT Pros & Developers. It's quick & easy.

Wrapping a class set method

P: n/a
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?

Jul 27 '05 #1
Share this Question
Share on Google+
3 Replies


P: n/a
On 27 Jul 2005 11:42:24 -0700, "snoe" <ca*********@gmail.com> wrote:
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
Jul 27 '05 #2

P: n/a
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
Jul 27 '05 #3

P: n/a
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()

Jul 27 '05 #4

This discussion thread is closed

Replies have been disabled for this discussion.