我想创建一个包含一些动态键的专用字典。对dict
进行子类化并覆盖__getitems__
会使我获得大部分内容,但是您只能从上次直接访问中获取密钥的值。
$ cat schrodingers_dict.py
cats = []
class SchrodingersDict(dict):
def __getitem__(self, key):
if key == 'dead' or key == 'alive':
self[key] = cats.count(key)
return super().__getitem__(key)
box = SchrodingersDict([('dead', 0), ('alive', 0)])
$ python3 -i schrodinger_dict.py
>>> box
{'dead': 0, 'alive': 0}
>>> cats.append('alive')
>>> cats.append('dead')
>>> cats.append('dead')
>>> box
{'dead': 0, 'alive': 0}
>>> box['dead']
2
>>> box['alive']
1
>>> box
{'dead': 2, 'alive': 1}
是否可以让间接访问返回动态值,例如repr(box)
或json.dumps(box)
?
答案 0 :(得分:1)
你应该扩展MutableMapping
并实现相关的抽象方法(委托给非计算密钥的具体底层字典),因为由于性能优化dict
不能保证调用{{1例如迭代k / v。
__getitem__
您可能还希望将计算出的密钥设置为完全虚拟,而不是将它们存储在底层字典中,无论如何都要在每次访问时重新计算它们,还可以节省一些指令和空间。
答案 1 :(得分:0)
目标是以某种方式在访问'dead'
的值时自动计算'alive'
和dict
的值。
但是,实现对values
内某种__getattribute__
属性的访问权限的检查是不可能的,因为dict
是一个(基于C的)python内置并且没有这样的属性。此外,由于速度优化,特殊方法查找会绕过__getattribute__
。
不幸的是,dict
访问值的方法没有公开的常用方法(values
,items
只返回视图,一切都在内部处理),所以有必要所有这些方法都要事先明确更新。
一种解决方案是以
的方式覆盖每个这样的函数def accessing_method(self):
self._update_dead_and_alive()
return super().accessing_method()
另一个是以编程方式
from functools import wraps
cats = []
def add_state_update(cls):
# all the methods accessing values of dict
accmethods_names = [
'__repr__', '__getitem__', 'get', 'items', 'pop', 'popitem',
'setdefault', 'values'
]
def state_update(method):
def update_dead_and_alive(self):
self['dead'] = cats.count('dead')
self['alive'] = cats.count('alive')
@wraps(method)
def wrapper(*args, **kwargs):
update_dead_and_alive(*args)
return method(*args, **kwargs)
return wrapper
# put update_dead_and_alive in front of the execution of every
# accmethod
for n in accmethods_names:
method = getattr(cls, n)
setattr(cls, n, state_update(method))
return cls
@add_state_update
class SchrodingersDict(dict):
pass
box = SchrodingersDict([('dead', 0), ('alive', 0)])
根据上述功能进行测试时,会得到以下结果
>>> cats.append('alive')
>>> cats.append('dead')
>>> cats.append('dead')
>>>
>>> import pickle
>>> pickle.loads(pickle.dumps(box))
{'dead': 2, 'alive': 1}
>>>
>>> cats.append('alive')
>>> import json
>>> json.loads(json.dumps(box))
{'dead': 2, 'alive': 2}
>>>
>>> cats.append('alive')
>>> eval(repr(box)) == box
True
第一个版本会产生相似数量的代码,可能更明确。