私は質問を見たときにこれがどのように達成されるのか不思議でしたが、私が思いついた解決策はここにあります。私が望むほどシンプルではありませんが、役に立つかもしれません。まず、ここでの動作は次のとおりです。
class Tracker(object):
def __init__(self):
self.lst = trackable_type('lst', self, list)
self.dct = trackable_type('dct', self, dict)
self.revisions = {'lst': [], 'dct': []}
>>> obj = Tracker() # create an instance of Tracker
>>> obj.lst.append(1) # make some changes to list attribute
>>> obj.lst.extend([2, 3])
>>> obj.lst.pop()
3
>>> obj.dct['a'] = 5 # make some changes to dict attribute
>>> obj.dct.update({'b': 3})
>>> del obj.dct['a']
>>> obj.revisions # check out revision history
{'lst': [[1], [1, 2, 3], [1, 2]], 'dct': [{'a': 5}, {'a': 5, 'b': 3}, {'b': 3}]}
今、このすべてを可能にするtrackable_type()
機能:
def trackable_type(name, obj, base):
def func_logger(func):
def wrapped(self, *args, **kwargs):
before = base(self)
result = func(self, *args, **kwargs)
after = base(self)
if before != after:
obj.revisions[name].append(after)
return result
return wrapped
methods = (type(list.append), type(list.__setitem__))
skip = set(['__iter__', '__len__', '__getattribute__'])
class TrackableMeta(type):
def __new__(cls, name, bases, dct):
for attr in dir(base):
if attr not in skip:
func = getattr(base, attr)
if isinstance(func, methods):
dct[attr] = func_logger(func)
return type.__new__(cls, name, bases, dct)
class TrackableObject(base):
__metaclass__ = TrackableMeta
return TrackableObject()
これは基本的にあれば、いくつかのリビジョンのログを追加するには、オブジェクトのすべてのメソッドをオーバーライドするメタクラスを使用していますオブジェクトが変更されます。これは徹底的にテストされたスーパーではないと私はlist
とdict
以外の他のオブジェクトタイプを試していないが、それらのために大丈夫と思われる。
どの程度期待していませんオブジェクトをインターンにして許可するゲッターとセッターのクラスだけでは外部からのアクセスですか? – joni
オブジェクトをシリアル化してシリアル化されたバージョンを比較することができます。 –
私はjoniに同意します。ここではデメテルの法則について考えてみましょう。あるいは、 'attr'へのアクセスを直接禁止するが、ディープコピーを作成するgetterとsetterを提供する。 – Thomas