Python Schrodingers字典:获取间接访问上的动态键的值
我想创建一个有一些动态键的专门词典。子类化Python Schrodingers字典:获取间接访问上的动态键的值,python,python-3.x,dictionary,Python,Python 3.x,Dictionary,我想创建一个有一些动态键的专门词典。子类化dict和重写\uuuu getitems\uuuuu使我大部分时间都在那里,但是您只能从最后一次直接访问中获取key的值 $ cat schrodingers_dict.py cats = [] class SchrodingersDict(dict): def __getitem__(self, key): if key == 'dead' or key == 'alive': self[key] =
dict
和重写\uuuu getitems\uuuuu
使我大部分时间都在那里,但是您只能从最后一次直接访问中获取key的值
$ 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)
您可能应该扩展MutableMapping
并实现相关的抽象方法(将非计算键委托给一个具体的底层dict),因为由于性能优化dict
不能保证在k/v上迭代时调用\u getitem\uuuuuuu
$ cat schrodingers_dict.py
from collections import MutableMapping
cats = []
class SchrodingersDict(MutableMapping):
def __init__(self, data):
self._store = dict(data)
def __getitem__(self, key):
if key == 'dead' or key == 'alive':
self._store[key] = cats.count(key)
return self._store[key]
def __setitem__(self, key, value):
self._store[key] = value
def __delitem__(self, key):
del self._store[key]
def __iter__(self):
return iter(self._store)
def __len__(self):
return len(self._store)
def __repr__(self):
return repr(dict(self.items()))
box = SchrodingersDict([('dead', 0), ('alive', 0)])
cats.append('dead')
cats.append('dead')
cats.append('alive')
$ python3 -i schrodinger_dict.py
>>> box
{'dead': 2, 'alive': 1}
您可能还希望计算出的密钥是完全虚拟的,而不是将它们存储在基础dict中,您在每次访问时都要重新计算它们,还可以节省一些指令和空间。目标是在访问
dict
的值时,以某种方式自动计算的“死”值和的“活”值
然而,在\uuuu getattribute\uuuuu
中实现对某种值的访问检查是不可能的,因为dict
是一个(基于C的)python内置属性,没有类似的属性。此外,由于速度优化,特殊方法查找会绕过\uuuu getattribute\uuuuu
不幸的是,dict
的方法没有公开的通用方法来访问值(值
,项
仅返回视图,所有内容都在内部处理),因此所有这些方法都需要事先显式更新
一种解决方案是以
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
第一个版本的代码量相似,可能更明确。我不确定是否理解正确。在本例中,只要cat的内容发生更改,repr(box)
或json.dumps(box)
的输出就应该更改?或者实际存储的值是否应该更改?@bbastu我希望在间接访问时更改该值,例如repr
,dumps
。当dict发生变化时,我不需要cat
向它发出信号,我只想在dict被显示或序列化时,该值是正确的。@iiiSeymour如果不是,json.dumps使用的是不会考虑虚拟键的iter/iteritems。由于您必须在映射上实现\uuuuu iter\uuuuu
,并且每个相关方法都是通过该映射路由的(和/或\uuuuuu getitem\uuuuuu
),因此您可以正确地将虚拟键添加到该映射中,并在其余部分使用内部dict,对于每个更高级别的方法,情况都会很好,对于dict
@iiSeymourdict
来说,如果重写其方法(可以绕过),则基本上不提供任何保证,映射需要实现一组特定的方法,并保证更高级别的调用将通过这些方法。