2011-12-06 13 views
4

我希望能夠告訴我的Python對象中的數據字段自某些事件以來是否被修改過。我以爲我會實現這個 使用性質類似如下:記錄對python對象的數據成員中元素的訪問

class C(object): 

    def get_scores(self): 
     print 'getter' 
     return self.__scores 

    def set_scores(self, value): 
     print 'setter' 
     self.__scores = value 
     self.__scoresWereChanged = True 

    scores = property(get_scores, set_scores, None, None) 

    def do_something(self): 
     if self.__scoresWereChanged: 
      self.__updateState() #will also set self.__scoresWereChanged to False 
     self.__do_the_job() 

這種方法效果很好,如果scores是不可改變的,但它scores是一個名單,我也改變單一的元素,這種方法失敗:

In [79]: c.scores = arange(10) 
setter 

In [80]: print c.scores 
getter 
Out[80]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

In [81]: c.scores[3] = 999 
getter #setter was not called 

我可以

In [84]: s = c.scores 
getter 

In [85]: s[3] = 2222 

In [86]: c.scores = s 
setter 

解決這個問題,但是,當然,這不會是因爲ellegant,我想。

+0

可以'__getattribute__'幫助你嗎?它攔截訪問對象的所有屬性 –

回答

2

可以這樣做 - 我能想到一個複雜的方法,其中每個賦值給「保護」屬性都會爲要存儲的對象創建一個動態包裝類。該類將監視對該對象的任何「魔術」方法的訪問(即__setitem__,__setattr__,__iadd__,__isub__等)。

動態創建這樣的類是有趣的,但棘手的,棘手的,以適應每種情況。

我能想到的另一個選擇是創建每個防護屬性的副本,並在每個讀取訪問時檢查存儲的屬性是否與其副本相同。如果沒有,它會標記爲髒。

而不是使用性質的,這將是清潔建立一個描述符類(它實現了「財產」之類的行爲)

from copy import deepcopy 

class guarded_property(object): 
    # self.name attribute set here by the metaclass 
    def __set__(self, instance, value): 
     setattr(instance, "_" + self.name, value) 
     setattr(instance, "_" + self.name + "_copy", deepcopy(value)) 
     setattr(instance, "_" + self.name + "_dirty", True) 

    def __get__(self, instance, owner): 
     return getattr (instance, "_" + self.name) 

    def clear(self, instance): 
     setattr(instance, "_" + self.name + "_dirty", False) 
     setattr(instance, "_" + self.name + "_copy", deepcopy(self.__get__(instance, None))) 

    def dirty(self, instance): 
     return getattr(instance, "_" + self.name + "_dirty") or not (getattr(instance, "_" + self.name + "_copy") == self.__get__(instance, None)) 


class Guarded(type): 
    def __new__(cls, name, bases, dict_): 
     for key, val in dict_.items(): 
      if isinstance(val, guarded_property): 
       val.name = key 
       dict_[key + "_clear"] = (lambda v: lambda self: v.clear(self))(val) 
       dict_[key + "_dirty"] = (lambda v: property(lambda self: v.dirty(self)))(val) 
     return type.__new__(cls, name, bases, dict_) 


if __name__ == "__main__": 
    class Example(object): 
     __metaclass__ = Guarded 
     a = guarded_property() 

    g = Example() 
    g.a = [] 
    g.a_clear() 
    print g.a_dirty 
    g.a.append(5) 
    print g.a_dirty 
    g.a_clear() 
    print g.a_dirty 
3

我會說沒有任何簡單的解決方案。

如果你打算使用清單,在你的榜樣,那麼你就需要繼承list和,至少,提供自己實現__setitem__方法來允許監測名單的變化。有關其他方法也被覆蓋的完整示例,請查看urwid library代碼。特別是,monitored_list.MonitoredList實現了類似的東西。

除此之外,您可能希望查看替代方法來定義代碼中的屬性,以便在屬性更新時提供通知事件,如traitsgobject(很老的教程,但仍然很好, )。