2012-05-19 11 views
5

Ho due proprietà che contiene liste. ogni volta che qualsiasi elemento in questo elenco cambia, vorrei che l'altro elenco si aggiornasse da solo. questo include la dichiarazione obj.myProp [3] = 5. in questo momento, questa istruzione chiama la funzione getter per ottenere l'intera lista, ottiene il terzo elemento dall'elenco e imposta a 5. l'elenco myProp viene modificato, ma il secondo elenco non viene mai aggiornato.python: come avere una proprietà e con una funzione setter che rileva tutte le modifiche che accadono al valore

Ecco il mio codice

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 

e qui è una corsa esempio.

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

se si guardano le ultime tre righe, questo è dove si verifica il problema reale. Ho impostato il primo elemento della sottolista su tre, ma foo.columns non si aggiorna mai per mettere il 3 nella sua lista.

quindi, in breve, come faccio a creare una variabile che aggiorna sempre un'altra variabile, anche quando si sta modificando il sottotitolo.

sto usando Python 2.7

risposta

5

Il tuo problema è che non sei impostazionefoo.rows sulla linea incriminata - sei ottenere, e poi modificando uno dei suoi membri. Questo non ha intenzione di licenziare il setter. Con l'API che stai proponendo, dovresti restituire una lista che ha anche getter e setter collegati.

Faresti meglio a non utilizzare le righe e le colonne proprietà per impostare le voci, e aggiungere un metodo __getiten__ come questo:

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 linea tratteggiata diventa quindi:

foo[0, 0] = 3 
2

se semplicemente restituire il rows o columns lista, allora non dovrete mai alcun controllo su ciò che accade quando un elemento è cambiato.

una possibilità non sarebbe quella di fornire proprietà per ottenere/impostare gli elenchi direttamente, ma fornire setter/getter per un elemento in posizione x/y.

una versione bello sarebbe avere __setitem__/__getitem__ e li hanno accettare una tupla, in questo modo si potrebbe acces gli elementi utilizzando foo[x,y] e foo[x,y] = bar.

un altro modo sarebbe quello di restituire un wrapper attorno alla lista che rileva che un elemento è stato modificato, ma poi dovrai farlo anche per ogni elenco annidato.

0

I consigliamo di esaminare usando numpy. In particolare, il metodo fornisce una "vista" della matrice sottostante con gli assi trasposti, il che significa che se si cambia uno, l'altro cambierà.

Se ciò non soddisfa le tue esigenze, allora l'unico modo che posso vedere per rendere l'API che vuoi funzionare è definire i metodi "righe" e "colonne" per restituire "oggetti vista" personalizzati che non in realtà memorizza i dati, ma riporta ad alcuni dati privati ​​(condivisi) nell'oggetto griglia stesso. Questi potrebbero essere simili a liste, ma non supportano alcune operazioni (ad esempio, aggiungi o cancella).

Problemi correlati