Python でリストと辞書の変更を追跡するには? 質問する

Python でリストと辞書の変更を追跡するには? 質問する

このクラスのインスタンスがその属性の変更を追跡する必要があるクラスがあります。

例: は、の をobj.att = 2オーバーライドするだけで簡単に追跡できるようになります。__setattr__obj

ただし、変更したい属性がリストや辞書などのオブジェクト自体である場合は問題が発生します。

obj.att.append(1)またはのようなものを追跡するにはどうすればよいでしょうかobj.att.pop(2)?

リストまたは辞書クラスを拡張することを考えていますが、 と の両方が初期化されたら、それらのクラスのインスタンスをモンキーパッチして、objなどobj.attが呼び出されたobjときに通知されるようにします.append。どういうわけか、それはあまりエレガントではないようです。

私が考えられる他の方法は、 のインスタンスをobjリストの初期化に渡すことですが、これでは多くの既存のコードが壊れ、前の方法よりもさらに洗練されていないようです。

他に何かアイデアや提案はありますか? 私が見逃している簡単な解決策はありますか?

ベストアンサー1

質問を見たときに、これをどうやって実現できるのか興味があったので、私が思いついた解決策を紹介します。思ったほど単純ではありませんが、役に立つかもしれません。まず、動作は次のとおりです。

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()

これは基本的に、メタクラスを使用してオブジェクトのすべてのメソッドをオーバーライドし、オブジェクトが変更された場合にリビジョン ログを追加します。これは徹底的にテストされたわけではなく、 と 以外のオブジェクト タイプを試したことはありませんlistdict、それらに対しては問題なく動作するようです。

おすすめ記事