self-aware list of objects able to sense constituent member alterations?

John O'Hagan mail at johnohagan.com
Mon Feb 9 01:45:39 EST 2009


On Fri, 6 Feb 2009, greywine at gmail.com wrote:
> On Jan 28, 4:37 am, John O'Hagan <m... at johnohagan.com> wrote:
> > On Tue, 27 Jan 2009, Reckoner wrote:
> > > I'm not sure this is possible, but I would like to have
> > > a list of  objects
> > >
> > > A=[a,b,c,d,...,z]
> > >
> > > where,  in the midst of a lot of processing I might do something like,
> > >
> > > A[0].do_something_which_changes_the_properties()
> > >
> > > which alter the properties of the object 'a'.
> > >
> > > The trick is that I would like A to be mysteriously aware that
> > > something about the  object 'a' has changed so that when I revisit A,
> > > I will know that the other items in the list need to be refreshed to
> > > reflect the changes in A as a result of changing 'a'.
> > >
> > > Even better would be to automatically percolate the subsequent changes
> > > that resulted from altering 'a' for the rest of the items in the list.
> >
> > [...]
> >
> > Interesting question.
> >
> > Maybe this is too simple for your purpose (or maybe just wrong!), but
> > could you subclass list and give it an "update" method which keeps a
> > dictionary of the state of its members and/or calls another method that
> > makes the appropriate changes in the other members when a change occurs,
> > something like:
> >
> > class SelfAwareList(list):
> >
> >     state_dict = {}
> >
> >     def update(self):
> >         for i in self:
> >             if i.state == 'some_condition':
> >                 self.do_stuff_to_other_members()
> >             self.state_dict[i] = i.state
> >
> >     def do_stuff_to_other_members(self):
> >         print 'doing stuff...'
> >
> > ?
> >
> > You could manually call update() on the SelfAwareList instance after
> > calling a method on a SelfAwareList member, or even build it into the
> > members' methods so that it was automatic.
> >
> > HTH,
> >
> > John
>
> Hi Reckoner & John O'Hagan,
>
> Great thread, very interesting.
>
> John, I haven't seen anything like your class that uses list instead
> of object and refers to state directly (i.state in self).  Could you
> elaborate?  Here would be my implementation of your idea:
>
> class SelfAwareList2(object):
>
>     def __init__(self, l):
>         self.l = l
>         self.changed = [False for element in l]
>
>     def __str__(self):
>         return str(self.l)
>
>     def update(self, index, value):
>         self.l[index] = value
>         self.changed[index] = True
>
>     def do_stuff_to_other_members(self):
>         for i in self.changed:
>             if i==False:
>                 self.l[i] += 1
>
> Here you can print and whenever you update your list, self.changed
> keeps track of what changed.  Later on you can call do_stuff_to_others
> which in this case adds 1 to each other element.
>
> I assume that your class replaces the awkwardness of l.update(0, 5)
> with l[0] = 5, but I can't quite wrap my mind around it.

I think my suggestion was much more simplistic than what the OP was after, 
which was far more thoroughly answered by others.

But as far as my version goes, your implementation is fine as far as I can 
tell; although I (possibly mistakenly) interpreted the OP as wanting 
something which kept track of _how_ the items in the list changed, not just 
_if_ they changed.

Trying to put together an example was (naturally) more complicated than I 
thought, but here's a silly one, with apologies to Steven D'Aprano among 
others for stealing the parrot theme:

class Parrot(object):

    def __init__(self, name):
        self.color = 'red'
        self.name = name
    def __repr__(self):
        return self.name

class SelfAwareListOfParrots(list):

    colors = ['red', 'blue', 'yellow', 'green', 'black', 'white']
    state_dict = {}
    
    def update(self, changed=None):
        """Ensure no duplicate colors and record changes"""
        for parrot in self:
            used_colors = [parrot.color for parrot in self]
            available_colors = [color for color in self.colors
                if color not in used_colors]
            if (parrot is not changed and 
                used_colors.count(parrot.color) > 1):          	
                parrot.color = available_colors[0]	
            self.state_dict[parrot] = parrot.color
                
    def change(self, parrot, color):
	"""Change color and update"""
        self[self.index(parrot)].color = color
        self.update(parrot)

This list knows when a parrot has had its color changed by calling change() on 
the list, and makes sure no parrots are the same color by changing any 
parrots of duplicate color. It has a state_dict attribute  (redundant in this 
example) which keeps track of the colors after an update. (My version of the 
update method updates the whole list). 

The trick is to have the method that changes the elements of the list defined 
in the list class definition, rather than that of the elements' class, that 
way it's easy for the list to know about changes to its elements.

(If a parrots color is changed directly, update() will still ensure unique 
colors, but will not preserve the change unless that parrot is given as an 
argument.)

For example:

polly = Parrot('polly')
molly = Parrot('molly')
dolly = Parrot('dolly')

salp = SelfAwareListOfParrots([polly, molly, dolly])

print salp.state_dict

>>>{molly: 'yellow', polly: 'white', dolly: 'black'}

salp.change(polly, 'black')

print salp.state_dict

>>>{molly: 'yellow', polly: 'black', dolly: 'white'}

As for the subclassing of list, that allows you to create a class with all the 
methods of a list, to which you can add your own. (Although any list methods 
which return a new list will return a list, not an instance of your class, 
unless you define them to do so in your class definition). 

In my post quoted above, by "i.state" I was probably abusing the term "state" 
to signify "some arbitrary attribute" of i.

I'm pretty new to this stuff, but there are plenty of tutorials on it out 
there, and I'm relying on others to correct any fallacies I may be 
unwittingly propagating here. :)

Regards,

John



More information about the Python-list mailing list