我继承了一堆旧代码,遇到了一些重大问题。我相信这里尝试的想法是使用容器化的缓存(例如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'}
答案 0 :(得分:0)
第self.b["foo"] = "bar"
行是从自身中获取属性b
,其中b
被假定为字典,并分配给键{{1} }在上述字典中。只有将 直接分配给b时,"foo"
才会被调用,例如__setattr__
(在Python 3.5+中用于字典更新)。
无需在存储的对象中添加钩子以触发其self.b = {**self.b, 'foo': 'bar'}
及其子对象__setattr__
等上的缓存保存,您可以添加一种显式触发缓存更新的方法(这意味着用户代码需要了解缓存),或者避免通过直接分配来更改字段。
对不起,您很幸运;)。
答案 1 :(得分:0)
问题在于您要在不实现对象写回的情况下重新实现搁置模块。
何时:
self.b = dict()
在缓存中将__setattr__
存储一个腌制的字典:
然后何时:
self.b["foo"] = "bar"
__getattribute__
将字典解开并返回它。然后,Python将该字典的foo键设置为bar。然后它扔掉了字典。