Python字典 - 二进制搜索密钥?

时间:2010-07-02 02:11:36

标签: python

我想编写一个像字典一样的容器类(实际上是从字典派生的),这个结构的键是日期。

当使用密钥(即日期)从类中检索值时,如果日期不存在,则使用密钥之前的下一个可用日期来返回该值。

以下数据应该有助于进一步解释这个概念:

Date (key)      Value
2001/01/01      123
2001/01/02       42
2001/01/03      100
2001/01/04      314
2001/01/07      312
2001/01/09      321

如果我尝试获取与密钥(日期)'2001/01/05'相关联的值,我应该获得存储在密钥2001/01/04下的值,因为该密钥发生在密钥'2001/01 /之前05'如果它存在于字典中就会存在。

为了做到这一点,我需要能够进行搜索(理想情况下是二进制,而不是天真地遍历字典中的每个键)。我在Python词典中搜索过bsearch词典键查找 - 但是没有找到任何有用的东西。

无论如何,我想写一个这样的类来封装这种行为。

这是我到目前为止(不多):

#
class NearestNeighborDict(dict):
#
"""
#
a dictionary which returns value of nearest neighbor 
if specified key not found
#
"""

def __init__(self, items={}):
    dict.__init__(self, items)


def get_item(self, key):
    # returns the item stored with the key (if key exists)
    # else it returns the item stored with the key

5 个答案:

答案 0 :(得分:13)

您真的不想继承dict,因为您无法真正重用其任何功能。相反,如果你想在创建之后也能够修改一个实例,那么继承抽象基类collections.Mapping(或MutableMapping),为此目的实现必不可少的特殊方法,你将获得其他{ {1}} - 类似ABC的“免费”方法。

您需要编码的方法是dict__getitem____setitem__,如果您想要可变性),__delitem____len__和{{1 }}

标准库的bisect模块为您提供了在排序列表之上有效实现这些功能所需的一切。例如......:

__iter__

你可能想要根据你想要返回的内容(或者你是否想要提高)来调整__contains__,以避免各种角落情况,例如“import collections import bisect class MyDict(collections.Mapping): def __init__(self, contents): "contents must be a sequence of key/value pairs" self._list = sorted(contents) def __iter__(self): return (k for (k, _) in self._list) def __contains__(self, k): i = bisect.bisect_left(self._list, (k, None)) return i < len(self._list) and self._list[i][0] == k def __len__(self): return len(self._list) def __getitem__(self, k): i = bisect.bisect_left(self._list, (k, None)) if i >= len(self._list): raise KeyError(k) return self._list[i][1] 大于{{1}中的所有键}}”。

答案 1 :(得分:5)

sortedcontainers模块提供SortedDict类型,可以按排序顺序维护密钥,并支持对这些密钥进行二等分。该模块是纯Python和fast-as-C implementations,具有100%的测试覆盖率和数小时的压力。

例如:

from sortedcontainers import SortedDict

sd = SortedDict((date, value) for date, value in data)

# Bisect for the index of the desired key.
index = sd.bisect('2001/01/05')

# Lookup the real key at that index.
key = sd.iloc[index]

# Retrieve the value associated with that key.
value = sd[key]

由于SortedDict支持快速索引,因此您也可以轻松地向前或向后查看。 SortedDict也是一个MutableMapping,所以它应该在你的类型系统中很好地工作。

答案 2 :(得分:0)

我会扩展dict,并覆盖__getitem____setitem__方法来存储已排序的键列表。

from bisect import bisect

class NearestNeighborDict(dict):
    def __init__(self):
        dict.__init__(self)
        self._keylist = []

    def __getitem__(self, x):
        if x in self:
            return dict.__getitem__(self, x)

        index = bisect(self._keylist, x)
        if index == len(self._keylist):
            raise KeyError('No next date')

        return dict.__getitem__(self, self._keylist[index])

    def __setitem__(self, x, value):
        if x not in self:
            index = bisect(self._keylist, x)
            self._keylist.insert(index, value)

        dict.__setitem__(self, x, value)

你最好继承MutableMapping,但原则是一样的,上面的代码很容易适应。

答案 3 :(得分:0)

为什么不直接从dict.keys()维护一个排序列表并搜索?如果你是dict的子类,你甚至可以设法在添加值时在该列表上进行二进制插入。

答案 4 :(得分:0)

在bintrees.RBTree:https://pypi.python.org/pypi/bintrees/2.0.1

上使用floor_key方法