2012-05-19 13 views
5

Tengo dos propiedades que tienen listas. cada vez que algún elemento de esta lista cambia, me gustaría que la otra lista se actualice a sí misma. esto incluye la declaración obj.myProp [3] = 5. en este momento, esta declaración llama a la función getter para obtener toda la lista, obtiene el tercer elemento de la lista y lo establece en 5. la lista myProp se cambia, pero la segunda lista nunca se actualiza.python: cómo tener una propiedad y con una función setter que detecta todos los cambios que ocurren con el valor

aquí es mi código

class grid(object): 

    def __init__(self,width=0,height=0): 

     #make self._rows a multi demensional array 
     #with it's size width*height 
     self._rows=[ [None]*height for i in xrange(width) ] 
     #make self._columns a multi demensional 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 collumns 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 

y aquí es un ejemplo de ejecución.

>>> 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 mira las últimas tres líneas, aquí es donde ocurre el problema real. Establecí el primer elemento de la sublista en tres, pero foo.columns nunca se actualiza para poner el 3 en su lista.

Así que, en resumen, ¿cómo hago una variable que siempre actualiza otra variable, incluso cuando se está cambiando el subelemento.

estoy usando Python 2.7

Respuesta

5

Su problema es que no estás establecerfoo.rows en la línea en cuestión - que está recibiendo , y luego modificando uno de sus miembros. Esto no va a disparar al colocador. Con la API que estás proponiendo, necesitarías devolver una lista que tenga captadores y ajustadores adjuntos también.

Harías mejor no usar las filas y columnas de propiedades para configurar las entradas, y añadir un método __getiten__ así:

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 
2

si simplemente devuelve su lista rows o columns, entonces usted nunca tendrá ningún control sobre lo que ocurre cuando se cambia un elemento.

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

una versión agradable sería tener __setitem__/__getitem__ y hacer que se acepte una tupla, de esa manera se podría acces los elementos utilizando foo[x,y] y foo[x,y] = bar.

de otra manera sería devolver un contenedor alrededor de la lista que detecta cuándo se cambia un elemento, pero también tendrá que hacer eso para cada lista anidada.

0

I Te recomendamos que estudies el uso de numpy. En particular, el método transpose 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 ajusta a sus necesidades, entonces la única manera que puedo ver para hacer que la API que desea trabajar sea definir los métodos "filas" y "columnas" para devolver "objetos de vista" personalizados que no en realidad, almacena los datos, pero eso apunta a algunos datos privados (compartidos) en el objeto de la grilla en sí. Pueden ser similares a listas, pero no son compatibles con algunas operaciones (por ejemplo, agregar o borrar).

Cuestiones relacionadas