如何实现可变更新时自动更新的可变PickleTypes

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何实现可变更新时自动更新的可变PickleTypes相关的知识,希望对你有一定的参考价值。

SQLAlchemy提供PickleType并为任何可变类型(如dict)提供mutation tracking

SQLAlchemy文档提到这是实现可变PickleType的方法,但它没有说明如何继续它。

注意:我想在PickleType中存储一个字典。

你是如何实现的?

答案

虽然文档提到了一些例子,但在我看来这还不够,所以我将在这里添加我的实现,可以用来实现一个被pickle并存储在数据库中的可变dict。

使用文档中的MutableDict示例:

class MutableDict(Mutable, dict):

    @classmethod
    def coerce(cls, key, value):
        if not isinstance(value, MutableDict):
            if isinstance(value, dict):
                return MutableDict(value)
            return Mutable.coerce(key, value)
        else:
            return value

    def __delitem(self, key):
        dict.__delitem__(self, key)
        self.changed()

    def __setitem__(self, key, value):
        dict.__setitem__(self, key, value)
        self.changed()

    def __getstate__(self):
        return dict(self)

    def __setstate__(self, state):
        self.update(self)

现在创建一个要跟踪的列:

class MyModel(Base):
    data = Column(MutableDict.as_mutable(PickleType))

我想看一些其他可能更高级或可能使用不同数据结构的示例。 pickle的通用方法是什么样的?有一个(我想不会,或者SQLAlchemy会有一个)。

另一答案

这是我提出的解决方案。它包装任何类型并检测任何属性集并调用Mutable.changed()。它还包装函数调用并通过在对象之前和之后拍摄对象的快照来检测更改。应该适用于Pickleable类型......

from sqlalchemy.ext.mutable import Mutable

class MutableTypeWrapper(Mutable):
    top_attributes = ['_underlying_object',
                      '_underlying_type',
                      '_last_state', 
                      '_snapshot_update', 
                      '_snapshot_changed', 
                      '_notify_if_changed',
                      'changed',
                      '__getstate__',
                      '__setstate__',
                      'coerce']

    @classmethod
    def coerce(cls, key, value):
        if not isinstance(value, MutableTypeWrapper):
            try:
                return MutableTypeWrapper(value)
            except:
                return Mutable.coerce(key, value)
        else:
            return value

    def __getstate__(self):
        return self._underlying_object

    def __setstate__(self, state):
        self._underlying_type = type(state)
        self._underlying_object = state

    def __init__(self, underlying_object, underlying_type=None):
        if (underlying_object is None and underlying_type is None):  
            print('Both underlying object and type are none.')
            raise RuntimeError('Unable to create MutableTypeWrapper with no underlying object or type.')

        if (underlying_object is not None):
            self._underlying_object = underlying_object
        else:
            self._underlying_object = underlying_type()

        if (underlying_type is not None):
            self._underlying_type = underlying_type
        else:
            self._underlying_type = type(underlying_object)

    def __getattr__(self, attr):
        if (attr in MutableTypeWrapper.top_attributes):
            return object.__getattribute__(self, attr)

        orig_attr = self._underlying_object.__getattribute__(attr)
        if callable(orig_attr):
            def hooked(*args, **kwargs):
                self._snapshot_update()
                result = orig_attr(*args, **kwargs)
                self._notify_if_changed()
                # prevent underlying from becoming unwrapped
                if result == self._underlying_object:
                    return self
                return result
            return hooked
        else:
            return orig_attr

    def __setattr__(self, attr, value):
        if (attr in MutableTypeWrapper.top_attributes):
            object.__setattr__(self, attr, value)
            return

        self._underlying_object.__setattr__(attr, value)

        self.changed()

    def _snapshot_update(self):
        self._last_state = pickle.dumps(self._underlying_object,
                                        pickle.HIGHEST_PROTOCOL)

    def _snapshot_changed(self):
        return self._last_state != pickle.dumps(self._underlying_object,
                                                pickle.HIGHEST_PROTOCOL)

    def _notify_if_changed(self):
        if (self._snapshot_changed()):
            self.changed()

然后将它与PickleType一起使用,如下所示:

class TestModel(Base):
    __tablename__ = 'testtable'

    id = Column(Integer, primary_key=True)
    obj = Column(MutableTypeWrapper.as_mutable(PickleType))

这里的缺点是在每次函数调用之前对底层类进行快照,然后在之后比较更改以验证底层对象是否已更改。这将对性能产生重大影响。

另一种确保修改PickleType对象时更新它们的方法是在提交更改之前复制并分配它们。

以上是关于如何实现可变更新时自动更新的可变PickleTypes的主要内容,如果未能解决你的问题,请参考以下文章

更新深层不可变状态属性时,Redux 不更新组件

如何更新可变 HashMap 中的值?

如何在 Angular Controller 中观察可变参数

不可变状态更新。在 Redux 中更新对象数组

如何在可变映射中访问/初始化和更新值?

MSVC2015 更新 3 可变参数模板解决方法