Невозможно обновить атрибуты изменяемого экземпляра, когда __setattr__ и __getattribute__ переопределены в python - PullRequest
0 голосов
/ 21 декабря 2018

Я унаследовал кучу устаревшего кода и столкнулся с проблемой.Я полагаю, что идея, которая здесь была предпринята, состояла в том, чтобы использовать контейнерный кэш, такой как 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 ]

0 голосов
/ 21 декабря 2018

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

Когда:

self.b = dict()

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

Затем, когда:

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

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

0 голосов
/ 21 декабря 2018

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

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

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

...