Невозможно обновить атрибуты изменяемого экземпляра, когда __setattr__ и __getattribute__ переопределены в python

Я унаследовал кучу унаследованного кода и столкнулся с проблемой. Я полагаю, что идея, которая здесь была предпринята, состояла в том, чтобы использовать контейнерный кэш, такой как redis, для хранения определенных атрибутов, которые будут совместно использоваться многими процессами. Помимо того, был ли это лучший способ выполнить задачу, я наткнулся на то, что меня смутило. Если __getattribute__ а также __setattr__ методы переопределяются, как показано ниже, атрибуты изменяемого экземпляра не могут быть обновлены. Я ожидаю, что изменения, внесенные в self.b отражаться в print() вызов, но, как вы можете видеть из вывода, печатается только исходный пустой словарь. Шагая через self.b["foo"] = "bar" линия показывает, что вызов __getattribute__ сделано и правильный объект извлекается из кэша, но нет вызова __setattr__ словарь не обновляется. У кого-нибудь есть идеи, почему это может быть?

import dill

_reserved_fields = ["this_is_reserved"]

class Cache:
    _cache = {}

    def get(self, key):
        try:
            return self._cache[key]
        except KeyError:
            return None

    def set(self, key, value):
        self._cache[key] = value

    def exists(self, key):
        return key in self._cache


class Class(object):
    def __init__(self, cache):
        self.__dict__['_cache'] = cache
        self.a = 1
        self.a = 2
        print(self.a)
        self.b = dict()
        self.b["foo"] = "bar"  # these changes aren't set in the cache and seem to disappear
        print(self.b)
        self.this_is_reserved = dict()
        self.this_is_reserved["reserved_field"] = "value"  # this updates fine
        print(self.this_is_reserved)

    def __getattribute__(self, item):
        try:
            return object.__getattribute__(self, item)
        except AttributeError:
            key = "redis-key:" + item
            if not object.__getattribute__(self, "_cache").exists(key):
                raise AttributeError
            else:
                obj = object.__getattribute__(self, "_cache").get(key)
                return dill.loads(obj)

    def __setattr__(self, key, value):
        if key in _reserved_fields:
            self.__dict__[key] = value
        elif key.startswith('__') and key.endswith('__'):
            super().__setattr__(key, value)
        else:
            value = dill.dumps(value)
            key = "redis-key:" + key
            self._cache.set(key, value)

if __name__ == "__main__":
    cache = Cache()
    inst = Class(cache)

    # output
    # 2
    # {}
    # {'reserved_field': 'value'}

2 ответа

Линия self.b["foo"] = "bar" получает атрибут b от себя, где b Предполагается, что это словарь, и присвоение ключа "foo" в указанном словаре. __setattr__ будет вызываться только в том случае, если вы назначите непосредственно b, например, self.b = {**self.b, 'foo': 'bar'} (в Python 3.5+ для обновления словаря).

Без добавления хуков в ваших хранимых объектах, чтобы вызвать кэш-сохранение их __setattr__и их подобъекты __settattr__и т. д., вы можете добавить способ явного запуска обновления кэша (то есть пользовательский код должен знать о кэше) или избежать изменения полей, за исключением прямого назначения.

Извините за вашу удачу;).

Проблема в том, что вы повторно внедряете модуль полки без реализации обратной записи объекта.

Когда:

self.b = dict()

происходит __setattr__ сохраняет маринованный словарь в кеше:

Тогда, когда:

self.b["foo"] = "bar"

__getattribute__ Разбирает словарь и возвращает его. Затем Python устанавливает ключ foo этого словаря в bar. Затем он выбрасывает словарь.

Другие вопросы по тегам