2011-12-06 7 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 

私は、もちろん、これは私がしたいようellegantではありません、

In [84]: s = c.scores 
getter 

In [85]: s[3] = 2222 

In [86]: c.scores = s 
setter 

によってこの問題を解決することができますが。

+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は同様のものを実装しています。

さらに、traitsまたはgobjectのようなプロパティが更新されたときに通知イベントを提供するプロパティをコード内で定義する別の方法を見てみることもできます(実際に古いチュートリアルですが、 )。

関連する問題