2010-06-29 9 views
5

Sto cercando di utilizzare il decoratore @property di Python su un dettato in una classe. L'idea è che voglio che un determinato valore (chiamiamolo 'messaggio') venga cancellato dopo l'accesso. Ma voglio anche un altro valore (chiamiamolo 'last_message') per contenere l'ultimo messaggio impostato, e tenerlo fino a quando non viene impostato un altro messaggio. Nella mia mente, questo codice dovrebbe funzionare:Utilizzo di decorator @property in dicts

>>> class A(object): 
...  def __init__(self): 
...    self._b = {"message": "", 
...      "last_message": ""} 
...  @property 
...  def b(self): 
...    b = self._b 
...    self._b["message"] = "" 
...    return b 
...  @b.setter 
...  def b(self, value): 
...    self._b = value 
...    self._b["last_message"] = value["message"] 
... 
>>> 

Tuttavia, non sembra:

>>> a = A() 
>>> a.b["message"] = "hello" 
>>> a.b["message"] 
'' 
>>> a.b["last_message"] 
'' 
>>> 

Non sono sicuro di quello che ho fatto di male? Mi sembra che lo @property non funzioni come mi aspetterei che su dicts, ma forse sto facendo qualcosa di fondamentalmente sbagliato?

Inoltre, so che potrei semplicemente utilizzare i singoli valori nella classe. Ma questo è implementato come una sessione in un'applicazione web e ho bisogno che sia un dittico. Potrei fare questo lavoro, o fare in modo che l'intera sessione si opponga a fingere che sia un dittico, o usare le singole variabili e modificarlo per renderlo funzionante per tutto il resto della base di codice. Preferirei semplicemente farlo funzionare.

+2

dovresti inserire la tua risposta in una nuova _ (auto-) risposta_ invece di modificare nella tua _question_ –

+0

btw, invece di un normale "dict" potresti essere interessato all'utilizzo di ['__slots__ = ['messaggio', 'last_message '] '] (https://docs.python.org/2/reference/datamodel.html?highlight=slots#__slots__) se questi sono gli unici due (fissi) tasti. Oppure modifica un 'collections.namedtuple' –

risposta

9
class MyDict(dict): 
    def __setitem__(self,key,value): 
     if key=='message': 
      dict.__setitem__(self,'message','') 
      dict.__setitem__(self,'last_message',value) 
     else: 
      dict.__setitem__(self,key,value) 

class A(object): 
    def __init__(self): 
      self._b = MyDict({"message": "", 
         "last_message": ""}) 
    @property 
    def b(self): 
     return self._b 

a=A() 
a.b['message']='hello' 
print(a.b['message']) 
# '' 
print(a.b['last_message']) 
# hello 

Come penso che hai scoperto, il motivo per cui il setter non funzionava è perché

a.b['message']='hello' 

prima accessi a.b, che chiama il b getter della proprietà, non il suo setter. Il getter restituisce il dict self._b. Quindi self._b['message']='hello' viene chiamato il __setitem__ del dict.

Quindi per risolvere il problema, è necessario un dettato speciale (come MyDict).

+0

Grazie, ho fatto una dict speciale e ora funziona. Ho eliminato del tutto le proprietà e ho semplicemente spostato tutte le funzionalità nel dict, sebbene (con '__getitem__'). Aggiungerò anche il mio codice alla domanda. –

+1

Tu (e @Carson) dovresti usare 'super (MyDict, self) .__ setitem __ (chiave, valore)' invece di 'dict .__ setitem __ (self, key, value)' etc (altrimenti le sottoclassi delle tue con più antenati si imbatteranno in guaio) –

1

Mi può essere mancato quello che stai cercando di fare qui, ma questo risolve il tuo problema?

class A(object): 
    def __init__(self): 
     self._b = {'message':'', 
        'last_message': ''} 

    @property 
    def b(self): 
     b = self._b.copy() 
     self._b['message'] = '' 
     return b 

    @b.setter 
    def b(self, value): 
     self._b['message'] = value 
     self._b['last_message'] = value 


if __name__ == "__main__": 
    a = A() 
    a.b = "hello" 
    print a.b 
    print a.b 
    print a.b["last_message"] 

$ python dictPropTest.py 
{'last_message': 'hello', 'message': 'hello'} 
{'last_message': 'hello', 'message': ''} 
hello 
+0

close, ho appena scoperto che l'assegnazione a una chiave di un setter non funziona davvero. Stavo pensando che 'a.b ['message'] =" ciao "' avrebbe passato un dict contenente una chiave 'message' al setter, ma non è questo il caso. Ma quello che voglio davvero è poterlo assegnare a una chiave di un setter. –