python: cómo tener una propiedad y con una función de establecimiento que detecta todos los cambios que ocurren en el valor

Tengo una dos propiedades que contiene listas. Siempre que cambie cualquier elemento de esta lista, me gustaría que la otra lista se actualice. Esto incluye la statement obj.myProp[3]=5 . En este momento, esta statement llama a la función getter para obtener la lista completa, obtiene el tercer elemento de la lista y establece que se cambia la lista myProp , pero la segunda lista nunca se actualiza.

 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 

Ejecución de ejemplo:

 >>> 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]] 

Si observa las últimas tres líneas, aquí es donde ocurre el problema real. Puse el primer elemento de la lista secundaria a tres, pero foo.columns nunca se actualiza para poner el 3 en su lista.

En resumen, ¿cómo puedo crear una variable que siempre actualice otra variable, incluso cuando se modifica su subelemento?

Estoy usando Python 2.7

Su problema es que no está configurando foo.rows en la línea ofensiva; lo está recibiendo y luego modifica a uno de sus miembros. Esto no va a disparar al setter. Con la API que está proponiendo, también deberá devolver una lista que tenga adjuntos y definidores.

Sería mejor no usar las propiedades de las filas y columnas para establecer entradas, y agregar un método __getitem__ como este:

 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) ] 

La línea discontinua se convierte entonces en:

 foo[0, 0] = 3 

Si simplemente devuelve su lista de rows o columns , nunca tendrá ningún control sobre lo que sucede cuando se cambia un elemento.

una posibilidad sería no proporcionar propiedades para obtener / establecer las listas directamente, pero proporcionar establecedores / captadores para un elemento en la posición x / y.

una buena versión sería tener __setitem__ / __getitem__ y hacer que acepten una tupla, de esa forma podría acceder a los elementos usando foo[x,y] y foo[x,y] = bar .

otra manera sería devolver un envoltorio alrededor de la lista que detecte cuando se cambia un elemento, pero también tendrá que hacerlo para cada lista anidada.

Te recomiendo que busques en el uso de numpy . En particular, el método de transposición da una “vista” de la matriz subyacente con los ejes transpuestos, lo que significa que si cambia uno, el otro cambiará.

Si eso no se adapta a sus necesidades, entonces la única manera que puedo ver para hacer que la API que desea funcione es definir los métodos de “filas” y “columnas” para devolver “objetos de vista” personalizados que no almacenan los datos. , pero que apuntan a algunos datos privados (compartidos) en el objeto de la cuadrícula en sí. Estos podrían ser similares a una lista, pero no admitirían algunas operaciones (por ejemplo, agregar o borrar).