self-aware list of objects able to sense constituent memberalterations?

R

Reckoner

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.
Naturally, all of these items are related in some parent-child
fashion.

that might be a lot to ask, however.

Any advice appreciated.
 
C

Chris Rebert

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'.

Have you considered:

A.change_properties_of_item_and_know_about_it(0)

?

Cheers,
Chris
 
S

Steven D'Aprano

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'.

Can't be done if A is a built-in list, probably can't be done entirely
generically, but you can probably do it in a cooperative manner.


class TaintList(list):
tainted = False
def taint(self):
self.tainted = True
def untaint(self):
self.tainted = False

A = TaintList()


import functools
def taint(parent):
def decorator(func):
@functools.wraps(func)
def f(*args, **kwargs):
parent.taint()
return func(*args, **kwargs)
return f
return decorator


class TaintAwareThing(object):
def __init__(self):
self.attr = 0
@taint(A)
def change_attribute(self, x):
self.attr = x


x = TaintAwareThing()
y = TaintAwareThing()
z = TaintAwareThing()

A.extend([x, y, z])
A.tainted False
x.change_attribute(5)
A.tainted
True


Here is a second approach: create a proxy class TaintThing that wraps
whatever object you want, using delegation:

class TaintThing(object):
parent = A
def __init__(self, obj):
self.__dict__['_proxy'] = obj
def __getattr__(self, attr):
return getattr(self._proxy, attr)
def __setattr__(self, attr, value):
setattr(self._proxy, attr, value)
self.parent.taint()


Now change TaintList to automatically wrap anything stored in it:


# untested
class TaintList(list):
def append(self, obj):
list.append(self, TaintThing(obj))
# similar for __setitem__, extend, insert
 
A

Aaron Brady

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.
Naturally, all of these items are related in some parent-child
fashion.

that might be a lot to ask, however.

Any advice appreciated.

What you could do is specialize '__getitem__' (or '__getslice__') so
that whenever one of its items is accessed, the item is marked as
dirty or the entire list is refreshed. (Unproduced.)

def taintlist(list):
def __getitem__( self, key ):
x= super(taintlist, self).__getitem__( self, key )
self._dirty= True
self.refresh() #too early, unfortunately
return x
...

However, what you are probably after is something like this
(unproduced):

def taintlist(list):
def __getitem__( self, key ):
x= super(taintlist, self).__getitem__( self, key )
y= delegate( self, key, x )
return y

The 'delegate' class, also unproduced, automatically delegates
function calls (including member lookups) to the target. After the
delegated call returns, the list is notified-- hence the three
arguments to its constructor. (Unproduced.)

class delegate:
def __getattr__( self, key ):
attr= super( delegate, self ).__getattr__( self, key )
deleg= delegate( self.owner, self.ownerkey, attr )
return deleg
def __call__( self, *ar, **kw ):
res= self.attr( *ar, **kw )
self.owner.markdirty( )
return res

I'm not convinced it's possible, but there's a possibility... or
something like it. When you call a[0].meth(), three things happen:

x= '0' looked up on 'a'
y= 'meth' looked up on 'x'
z= 'y' called

You want control over the last of these parts, so you can call a
custom function instead. It becomes increasingly risky as the depth
increases, such as if the target class implements custom access, I
guess. In the 'delegate' shown, for example, it assumes that the
result is callable. You might need:

class delegate:
def __getattr__( self, key ):
attr= super( delegate, self ).__getattr__( self, key )
if not iscallable( attr ):
return attr
... #return delegate

Further, if the result is callable, that doesn't mean it will
necessarily be called. You should be able to tolerate this sequence:

x= '0' looked up on 'a'
y= 'meth' looked up on 'x'
z= attribute looked up on 'y' (instead of 'y' called)

Mind if we inquire after your progress?
 
J

John O'Hagan

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.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
 
K

koranthala

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.
Naturally, all of these items are related in some parent-child
fashion.

that might be a lot to ask, however.

Any advice appreciated.

I think Python Cookbook has a recipe which deals with this.
- 6.12 Checking an Instance for Any State Change.
 
K

koranthala

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.
Naturally, all of these items are related in some parent-child
fashion.
that might be a lot to ask, however.
Any advice appreciated.

I think Python Cookbook has a recipe which deals with this.
- 6.12 Checking an Instance for Any State Change.

Were you able to get this? If not, let me know. I will try to type it
in here - (it is a big recipe, so not doing it now)
 
P

Paul McGuire

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'.

Check out the Enthought Traits package.

-- Paul
 
R

Reckoner

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.
Naturally, all of these items are related in some parent-child
fashion.
that might be a lot to ask, however.
Any advice appreciated.
I think Python Cookbook has a recipe which deals with this.
- 6.12 Checking an Instance for Any State Change.

Were you able to get this? If not, let me know. I will try to type it
in here - (it is a big recipe, so not doing it now)

Actually, I have the python cookbook, but cannot find the recipe you
mention. maybe I have an older version?

thanks.
 
K

koranthala

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.
Naturally, all of these items are related in some parent-child
fashion.
that might be a lot to ask, however.
Any advice appreciated.
I think Python Cookbook has a recipe which deals with this.
- 6.12 Checking an Instance for Any State Change.
Were you able to get this? If not, let me know. I will try to type it
in here - (it is a big recipe, so not doing it now)

Actually, I have the python cookbook, but cannot find the recipe you
mention. maybe I have an older version?

thanks.

Mine is 2nd Edition.
 
R

Reckoner

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'.

Can't be done if A is a built-in list, probably can't be done entirely
generically, but you can probably do it in a cooperative manner.

class TaintList(list):
tainted = False
def taint(self):
self.tainted = True
def untaint(self):
self.tainted = False

A = TaintList()

import functools
def taint(parent):
def decorator(func):
@functools.wraps(func)
def f(*args, **kwargs):
parent.taint()
return func(*args, **kwargs)
return f
return decorator

class TaintAwareThing(object):
def __init__(self):
self.attr = 0
@taint(A)
def change_attribute(self, x):
self.attr = x
x = TaintAwareThing()
y = TaintAwareThing()
z = TaintAwareThing()
A.extend([x, y, z])
A.tainted False
x.change_attribute(5)
A.tainted

True

Here is a second approach: create a proxy class TaintThing that wraps
whatever object you want, using delegation:

class TaintThing(object):
parent = A
def __init__(self, obj):
self.__dict__['_proxy'] = obj
def __getattr__(self, attr):
return getattr(self._proxy, attr)
def __setattr__(self, attr, value):
setattr(self._proxy, attr, value)
self.parent.taint()

Now change TaintList to automatically wrap anything stored in it:

# untested
class TaintList(list):
def append(self, obj):
list.append(self, TaintThing(obj))
# similar for __setitem__, extend, insert

thanks for your reply.

For the second case where
class TaintThing(object):
parent = A
def __init__(self, obj):
self.__dict__['_proxy'] = obj
def __getattr__(self, attr):
return getattr(self._proxy, attr)
def __setattr__(self, attr, value):
setattr(self._proxy, attr, value)
self.parent.taint()

you have told it that parent is 'A'. Shouldn't that be passed to it
somehow in the following:
# untested
class TaintList(list):
def append(self, obj):
list.append(self, TaintThing(obj))
# similar for __setitem__, extend, insert

I apologize. I am probably missing something. This is getting pretty
advanced for me.

Thanks again.
 
S

Steven D'Aprano

thanks for your reply.

For the second case where
class TaintThing(object):
parent = A
def __init__(self, obj):
self.__dict__['_proxy'] = obj
def __getattr__(self, attr):
return getattr(self._proxy, attr)
def __setattr__(self, attr, value):
setattr(self._proxy, attr, value)
self.parent.taint()

you have told it that parent is 'A'. Shouldn't that be passed to it
somehow in the following:
# untested
class TaintList(list):
def append(self, obj):
list.append(self, TaintThing(obj))
# similar for __setitem__, extend, insert


Sure. Just change the initialisation of TaintThing to something like this:

#untested
def __init__(self, obj, parent):
self.__dict__['_proxy'] = obj
self.parent = parent


and then TaintList to something like this:

def append(self, obj):
list.append(self, TaintThing(obj, self))
# similar for __setitem__, extend, insert
 
P

Peter Wang

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.
Naturally, all of these items are related in some parent-child
fashion.

that might be a lot to ask, however.

Any advice appreciated.

You should really look at Enthought's Traits package. It does exactly
what you are asking for, and much, much more. See:

http://code.enthought.com/projects/traits/documentation.php
http://code.enthought.com/projects/traits/examples.php

Using Traits, you could do the following:

from enthought.traits.api import *
class Child(HasTraits):
state = Enum("happy", "sad", "bawling")

class Parent(HasTraits):
child = Instance(Child)

@on_trait_change('child.state')
def handler(self):
print "new child state:", self.child.state

bob_jr = Child()
bob = Parent(child = bob_jr)

bob_jr.state = "sad"
# This would result in bob.handler() being called

(Disclosure: I work at Enthought and have been using Traits heavily
for the last 4+ years.)

-Peter
 
R

Reckoner

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.
Naturally, all of these items are related in some parent-child
fashion.
that might be a lot to ask, however.
Any advice appreciated.

You should really look at Enthought's Traits package.  It does exactly
what you are asking for, and much, much more.  See:

http://code.enthought.com/projects/...de.enthought.com/projects/traits/examples.php

Using Traits, you could do the following:

from enthought.traits.api import *
class Child(HasTraits):
    state = Enum("happy", "sad", "bawling")

class Parent(HasTraits):
    child = Instance(Child)

    @on_trait_change('child.state')
    def handler(self):
        print "new child state:", self.child.state

bob_jr = Child()
bob = Parent(child = bob_jr)

bob_jr.state = "sad"
# This would result in bob.handler() being called

(Disclosure: I work at Enthought and have been using Traits heavily
for the last 4+ years.)

-Peter

I haven't looked at Enthought in awhile. I want to avoid having to
installing the entire Enthought toolsuite, however. Would I have to
do that for Traits?

Thanks again.
 
R

Reckoner

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.
Naturally, all of these items are related in some parent-child
fashion.
that might be a lot to ask, however.
Any advice appreciated.
I think Python Cookbook has a recipe which deals with this.
- 6.12 Checking an Instance for Any State Change.
Were you able to get this? If not, let me know. I will try to type it
in here - (it is a big recipe, so not doing it now)
Actually, I have the python cookbook, but cannot find the recipe you
mention. maybe I have an older version?

Mine is 2nd Edition.

for posterity's sake, here's the recipe in question:

import copy
class ChangeCheckerMixin(object):
containerItems = {dict: dict.iteritems, list: enumerate}
immutable = False
def snapshot(self):
''' create a "snapshot" of self's state -- like a shallow
copy, but
recursing over container types (not over general
instances:
instances must keep track of their own changes if
needed). '''
if self.immutable:
return
self._snapshot = self._copy_container(self.__dict__)
def makeImmutable(self):
''' the instance state can't change any more, set .immutable
'''
self.immutable = True
try:
del self._snapshot
except AttributeError:
pass
def _copy_container(self, container):
''' semi-shallow copy, recursing on container types only '''
new_container = copy.copy(container)
for k, v in self.containerItems[type(new_container)]
(new_container):
if type(v) in self.containerItems:
new_container[k] = self._copy_container(v)
elif hasattr(v, 'snapshot'):
v.snapshot( )
return new_container
def isChanged(self):
''' True if self's state is changed since the last snapshot
'''
if self.immutable:
return False
# remove snapshot from self.__dict__, put it back at the end
snap = self.__dict__.pop('_snapshot', None)
if snap is None:
return True
try:
return self._checkContainer(self.__dict__, snap)
finally:
self._snapshot = snap
def _checkContainer(self, container, snapshot):
''' return True if the container and its snapshot differ '''
if len(container) != len(snapshot):
return True
for k, v in self.containerItems[type(container)](container):
try:
ov = snapshot[k]
except LookupError:
return True
if self._checkItem(v, ov):
return True
return False
def _checkItem(self, newitem, olditem):
''' compare newitem and olditem. If they are containers, call
self._checkContainer recursively. If they're an instance
with
an 'isChanged' method, delegate to that method.
Otherwise,
return True if the items differ. '''
if type(newitem) != type(olditem):
return True
if type(newitem) in self.containerItems:
return self._checkContainer(newitem, olditem)
if newitem is olditem:
method_isChanged = getattr(newitem, 'isChanged', None)
if method_isChanged is None:
return False
return method_isChanged( )
return newitem != olditem

if __name__ == '__main__':
class eg(ChangeCheckerMixin):
def __init__(self, *a, **k):
self.L = list(*a, **k)
def __str__(self):
return 'eg(%s)' % str(self.L)
def __getattr__(self, a):
return getattr(self.L, a)
x = eg('ciao')
print 'x =', x, 'is changed =', x.isChanged( )
# emits: x = eg(['c', 'i', 'a', 'o']) is changed = True
# now, assume x gets saved, then...:
x.snapshot( )
print 'x =', x, 'is changed =', x.isChanged( )
# emits: x = eg(['c', 'i', 'a', 'o']) is changed = False
# now we change x...:
x.append('x')
print 'x =', x, 'is changed =', x.isChanged( )
# emits: x = eg(['c', 'i', 'a', 'o', 'x']) is changed = True
 
R

Robert Kern

I haven't looked at Enthought in awhile. I want to avoid having to
installing the entire Enthought toolsuite, however. Would I have to
do that for Traits?

No, Traits can be installed by itself unless if you want its GUI capabilities.

http://pypi.python.org/pypi/Traits

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
 
G

greywine

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.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 += 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.

Luther.
 
J

John O'Hagan

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.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 += 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

salp.change(polly, 'black')

print salp.state_dict

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
 

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
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top