python: how to have a property and with a setter function that detects all changes that happen to the value
properties, python
Solution
Your problem is that you're not setting `foo.rows` on the offending line - you're getting it, and then modifying one of it's members. This isn't going to fire the setter. With the API you're proposing, you would need to return a list that has getters and setters attached as well.
You'd do better to not use the rows and columns properties to set entries, and add a `__getitem__` method like this:
class Grid(object):
def __init__(self, width=0, height=0):
self._data = [None] * width * height;
self.width = width
self.height = height
def __getitem__(self, pos):
if type(pos) != tuple or len(pos) != 2:
raise IndexError('Index must be a tuple of length 2')
x, y = pos
if 0 <= x < self.width and 0 <= y < self.height:
return self._data[x + self.width * y]
else:
raise IndexError('Grid index out of range')
def __setitem__(self, pos, value):
if type(pos) != tuple or len(pos) != 2:
raise IndexError('Index must be a tuple of length 2')
x, y = pos
if 0 <= x < self.width and 0 <= y < self.height:
self._data[x + self.width * y] = value
else:
raise IndexError('Grid index out of range')
@property
def columns(self):
return [
[self[x, y] for x in xrange(self.width)]
for y in xrange(self.height)
]
@property
def rows(self):
return [
[self[x, y] for y in xrange(self.height)]
for x in xrange(self.width)
]
The broken line then becomes:
foo[0, 0] = 3
Problem
I have a two properties which holds lists. Whenever any item in this list changes, I would like the other list to update itself. This includes the statement `obj.myProp[3]=5`. Right now, this statement calls the getter function to get the whole list, gets the third item from the list, and sets that to 5. the `myProp` list is changed, but the second list never gets updated. ``` class Grid(object): def __init__(self,width=0,height=0): # Make self._rows a multi dimensional array # with it's size width * height self._rows=[[None] * height for i in xrange(width)] # Make `self._columns` a multi dimensional array # with it's size height * width self._columns=[[None] * width for i in xrange(height)] @property def rows(self): # Getting the rows of the array return self._rows @rows.setter def rows(self, value): # When the rows are changed, the columns are updated self._rows=value self._columns=self._flip(value) @property def columns(self): # Getting the columns of the array return self._columns @columns.setter def columns(self, value): # When the columns are changed, the rows are updated self._columns = value self._rows = self._flip(value) @staticmethod def _flip(args): # This flips the array ans=[[None] * len(args) for i in xrange(len(args[0]))] for x in range(len(args)): for y in range(len(args[0])): ans[y][x] = args[x][y] return ans ``` Example run: ``` >>> foo=grid(3,2) >>> foo.rows [[None, None], [None, None], [None, None]] >>> foo.columns [[None, None, None], [None, None, None]] >>> foo.rows=[[1,2,3],[10,20,30]] >>> foo.rows [[1, 2, 3], [10, 20, 30]] >>> foo.columns [[1, 10], [2, 20], [3, 30]] >>> foo.rows[0][0]=3 >>> foo.rows [[3, 2, 3], [10, 20, 30]] >>> foo.columns [[1, 10], [2, 20], [3, 30]] ``` If you look at the last three lines, this is where the actual problem occurs. I set the first item of the sublist to three, but `foo.columns` never updates itself to put the 3 in its list. So in short, how do I make a variable that always updates another variable, even when it's subitem is being changed? I'm using Python 2.7