我有一个字典'vcomments',其中键是非顺序整数。在循环键时,我需要按排序或反向排序顺序执行此操作。目前我使用
for key_pt in sorted(self.view.vcomments.iterkeys()):
但我还需要找到超出或超过某个数字的那些键(或下一个键):
if direction == 'down':
sorted_pts = (key_pt for key_pt in sorted(self.view.vcomments.iterkeys()) if key_pt > curr_pt)
else:
sorted_pts = (key_pt for key_pt in reversed(sorted(self.view.vcomments.iterkeys())) if key_pt < curr_pt)
try:
next_pt = sorted_pts.next()
except StopIteration:
我是否可以创建一个迭代器类(使用迭代器协议)来存储字典并使我能够以正向或反向顺序循环它们?我假设/猜测我可能需要先分配一个属性值,该值将指示下一个循环应该是正向/反向。
我可以在迭代器类中包含一个生成器函数(嵌套),使我能够检索下一个键;也就是说,在提供的整数之前或之前?
同样,我是否有办法提供开始和结束点并检索介于这些值之间的所有键(按排序顺序)?
我为问三个(虽然是相关的)问题而道歉 - 对第一个问题的回答会给我一个开始。我并不足以期待一个完整的解决方案,只是表明这些对我来说是否可行。
补充:我仍然需要能够通过其密钥检索单个特定的字典项。
答案 0 :(得分:4)
我认为这里最符合您需求的数据结构是skip list。我从来没有实现过 - 总是想要 - 但它看起来像这有你需要的所有东西。
跳过列表按排序顺序存储其项目。使基本列表成为双向链表将允许在O(n)中进行正向和反向迭代。
跳过列表允许O(log n)插入,修改,删除和搜索。这不像字典那么快,但在我看来,如果你需要按排序顺序存储的项目,字典会给你带来麻烦 - 即使是OrderedDict
,除非你非常很少添加密钥。
通过上面维基百科文章中描述的一些修改,即使是索引访问也可以在O(log n)中实现。
Python here中有一个实现 - 可能还有其他实现。
然而,你的一些评论表明,你可能只满足于简单地遍历你的字典的排序副本,而你只是想清理上面的代码。所以这是一种方法。这很天真,但这是一个起点。这假设你完全没有O(n)搜索时间和O(n log n)迭代次数,这两次都不是最理想的......
>>> class SortIterDict(dict):
... def __iter__(self):
... return iter(sorted(super(SortIterDict, self).__iter__()))
... def __reversed__(self):
... return reversed(tuple(iter(self)))
... def get_next(self, n):
... return next((x for x in iter(self) if x > n), None)
... def get_prev(self, n):
... return next((x for x in reversed(self) if x < n), None)
...
>>> d = SortIterDict({'d':6, 'a':5, 'c':2})
>>> list(d)
['a', 'c', 'd']
>>> list(reversed(d))
['d', 'c', 'a']
>>> d.get_next('b')
'c'
>>> d.get_prev('b')
'a'
答案 1 :(得分:2)
首先,您应该注意到您需要更好的数据结构。 Python dicts根本没有订单,OrderedDict
只保留了插入顺序(因此您需要对每个键更改进行重新排序)。像blist.sorteddict
这样的排序字典甚至像blist.sortedlist
这样的排序列表可能更适合您的需求。
我是否可以创建一个迭代器类(使用迭代器协议)来存储字典并使我能够以正向或反向顺序循环它们?我假设/猜测我可能需要先分配一个属性值,该值将指示下一个循环应该是正向/反向。
这里不需要单独的迭代器类。您可以通过内置的reversed
函数进行自由和向后迭代的迭代迭代:
for key in mydict:
# do something
for key in reversed(mydict.keys()):
# do something
我可以在迭代器类中包含一个生成器函数(嵌套),使我能够检索下一个键;也就是说,在提供的整数之前或之前?
当然,itertools
有许多功能可以让你做到这样的事情:
from itertools import dropwhile, takewhile
# find next key beyond 4
next(dropwhile(lambda x: x <= 4, mydict))
# find last key before 20
next(dropwhile(lambda x: x >= 20, reversed(mydict.keys()))
您也可以将其打包成一个函数:
def first_beyond(pivot, seq):
next(dropwhile(lambda x: x <= pivot, seq))
first_beyond(4, mydict)
first_beyond(20, reversed(mydict.keys()))
同样,我是否有办法提供开始和结束点并检索介于这些值之间的所有键(按排序顺序)?
您可以轻松地为此构建一个通用工具:
from itertools import dropwhile, takewhile
def between(begin, end, seq):
return takewhile(lambda x: x <= end,
dropwhile(lambda x: x < begin, seq))
要像这样使用:
>>> list(between(4, 30, [1,2,4,8,16,32]))
[4, 8, 16]
编辑:如果您只是需要偶尔检查已排序的键,您只需将它们转换为已排序的列表并使用它们即可。成语保持与上述相同:
keys = sorted(mydict)
# forward and backward iteration
for k in keys:
# ...
for k in reversed(keys):
# ...
# function that returns a forward or backward iterator based on an argument
def forward_or_backward(seq, forward=True):
for x in (iter if forward else reversed)(seq):
yield x
# random access inside a loop
for i, key in enumerate(keys):
# next element
key[i+1]
# the between and first_beyond functions above also work for lists
您可以将其余功能从这些部分粘合在一起。请注意,创建一个特殊的类是不明智的,因为我们可以以足够通用的方式编写函数,使它们可以处理任何可迭代的函数,而不仅仅是键列表。
答案 2 :(得分:1)
在这种情况下,我倾向于以两种不同的方式存储我的部分数据。
如果你保留了你的dict,但添加了一个由int引用的列表,它会显示你的dict的键(r值?)怎么办?这将为您提供您可能需要的随机访问(我假设您有一个原因的dict),以及您似乎需要添加的向后和向前行为。
如果你走这条路,你可以将它全部包装在一个类中,这样你就不会在你的代码中分散双重更新。
采用treap或red-black树实现可能是可行的,并修改它以允许您指定密钥,并在下一个或上一个密钥处获取密钥,值对。如果您经常插入或删除值,其中一个可能会更好。
答案 3 :(得分:0)
似乎有序的人可能会给你你想要的东西。文档为here。