Python:尝试创建包含有限MRU条目的dict
我试图创建一个Python:尝试创建包含有限MRU条目的dict,python,dictionary,mru,Python,Dictionary,Mru,我试图创建一个dict,它只包含有限数量的MRU条目(用于帮助缓存我通过ctypes调用的代价高昂的C函数的输出)。代码如下: from collections import OrderedDict class MRUDict(OrderedDict): def __init__(self, capacity = 64): super().__init__() self.__checkAndSetCapacity(capacity) def
dict
,它只包含有限数量的MRU条目(用于帮助缓存我通过ctypes调用的代价高昂的C函数的输出)。代码如下:
from collections import OrderedDict
class MRUDict(OrderedDict):
def __init__(self, capacity = 64):
super().__init__()
self.__checkAndSetCapacity(capacity)
def capacity(self):
return self.__capacity
def setCapacity(self, capacity):
self.__checkAndSetCapacity(capacity)
for i in range(len(self) - capacity):
self.__evict() # will execute only if len > capacity
def __getitem__(self, key):
value = super().__getitem__(key)
# if above raises IndexError, next line won't execute
print("Moving key {} to last i.e. MRU position".format(key))
super().move_to_end(key)
return value
def __setitem__(self, key, value):
if key in self:
super().move_to_end(key)
else: # new key
if len(self) == self.__capacity:
self.__evict()
super().__setitem__(key, value)
def __evict(self):
key, value = self.popitem(last = False) # pop first i.e. oldest item
print("Capacity exceeded. Evicting ({}, {})".format(key, value))
def __checkAndSetCapacity(self, capacity):
if not isinstance(capacity, int):
raise TypeError("Capacity should be an int.")
if capacity == 0:
raise ValueError("Capacity should not be zero.")
self.__capacity = capacity
。。。下面是测试代码:
def printkeys(d):
print("Current keys in order:", tuple(d)) # here d means d.keys()
print()
from mrudict import MRUDict
print("Creating MRUDict with capacity 5.")
d = MRUDict(5)
print("Adding keys 0 to 7 with values:")
for i in range(8): d[i] = i + 0.1
printkeys(d)
print("Calling str on object:")
print(d) # test of default __repr__ (since probably __str__ is the same here)
printkeys(d)
print("Accessing existing key 4:")
print(4, d[4]) # test of __getitem__
printkeys(d)
try:
print("Accessing non-existing key 20:")
print(20, d[20]) # test of __getitem__
except:
print("Caught exception: key does not exist.")
printkeys(d)
print("Updating value of existing key 6:")
d[6] = 6.6 # test of __setitem__ with existing key
printkeys(d)
print("Adding new key, value pair:")
d[10] = 10.1 # test of __setitem__ with non-existing key
printkeys(d)
print("Testing for presence of key 3:")
print(3 in d)
printkeys(d)
print("Trying to loop over the items:")
for k in d: print(k, d[k])
printkeys(d)
print("Trying to loop over the items:")
for k, v in d.items(): print(k, v)
printkeys(d)
现在从输出来看,我在实现\uu getitem\uuuuuuuuu
函数方面似乎有点幼稚,因为\uuuuuu repr\uuuuuu
和对于。。。在
(我猜在这里,调用\uuuuu iter\uuuuuuu
,然后调用\uuuu getitem\uuuuuuu
)会导致第一个项目作为MRU移动到最后一个,但无法继续,因为迭代器没有“下一个”项目,因为它现在指向最后一个元素。但我不确定我能做些什么来解决这个问题。我应该重新实施吗
我不知道如何区分用户的调用\uuuu getitem\uuuu
和内部调用。当然,一个解决方法是让用户使用一个find()
方法来完成移动到结尾的操作,但是我真的希望能够使用常规语法d[k]
请告知如何解决此问题。谢谢 对于这些复杂的行为变化,研究这些变化是值得的 实际的
\uuu iter\uuu
方法直接在内部结构上循环,即维护项目顺序的双链接列表。它永远不会直接使用\uuu getitem\uu
,而只是从链表返回键
您遇到的实际问题是,在循环时直接访问项目:
那里有一个d[k]
;正是这种访问将项目5从开始移动到结束。这会更新链接列表,因此当请求下一项时,curr.next
引用现在是根,迭代停止
解决办法是不要这样做。添加一个专用方法来访问项目,而不触发MRU更新。或者您可以重复使用dict.get()
,例如:
>>> for k in d: print(k, d.get(k))
...
5 5.1
7 7.1
4 4.1
6 6.6
10 10.1
您将遇到.items()
方法的问题orderedict
重用的.items()
方法,该方法返回一个实例;看
你必须改变这种行为:
from collections.abc import ItemsView
class MRUDictItemsView(ItemsView):
def __contains__(self, item):
key, value = item
v = self._mapping.get(key, object())
return v == value
def __iter__(self):
for key in self._mapping:
yield (key, self._mapping.get(key))
class MRUDict(OrderedDict):
# ...
def items(self):
return MRUDictItemsView(self)
您必须对
.keys()
和.values()
方法执行相同的操作。请您将问题解释得更清楚一些;您从当前测试中获得的输出与您想要/期望的结果有何不同?@jornsharpe:\uuu getitem\uuu
修改顺序。。因此,在迭代时,第一个项目移动到最后一个项目,迭代明显中断。在内部,orderedict
使用链式列表<代码>\uuuuuuuuuuuuuuuuuuuuuuuuuu直接遍历链表,没有调用\uuuuuuu getitem\uuuuuuuuu
。啊,.items()
重用可变映射.items()
,返回一个ItemsView()
对象,该对象在自定义类上使用\uuuuu getitem
。替换为不使用\uuuu getitem\uuuu
而是使用其他内部方法。现在我更新了答案,因为我有时间实际解析您的测试套件。供将来参考:请尝试将示例代码简化为能够立即演示问题的代码。您的大多数测试套件都与您的问题无关。您好,非常感谢您的详细回复,并对测试套件中的额外内容表示抱歉。现在我明白了我必须重新实现ItemsView
以避免使用[]
即\uuu getitem\uuuu
并改为使用.get()
,从您提供的集合.abc
链接可以清楚地看出,我也必须重新实现值view
,但鉴于键视图
似乎没有使用[]
我不需要重新实现它,对吗?好吧,我在Martijn给出的上面的代码的基础上所做的只是将一个,ValuesView
添加到collections.abc import行的,并在模块级添加一个mruditvaluesview
,替换ValuesView
方法中的.get()
,并将.values()
添加到mrudit
中,它现在可以工作了。非常感谢Martijn!对了,KeysView
确实不会触发\uGetItem\uuuuu
。
from collections.abc import ItemsView
class MRUDictItemsView(ItemsView):
def __contains__(self, item):
key, value = item
v = self._mapping.get(key, object())
return v == value
def __iter__(self):
for key in self._mapping:
yield (key, self._mapping.get(key))
class MRUDict(OrderedDict):
# ...
def items(self):
return MRUDictItemsView(self)