将std :: map映射到Python

时间:2009-09-29 07:14:54

标签: python dictionary

有时候有一个按键排序的字典是有意义的。在C ++中,这通常使用红黑树实现。但任何自我平衡的二元搜索树都会这样做(fwiw,Knuth在这个问题上特别清楚)。到目前为止我能够提出的最好的解决方案是使用R. McGraw's AVL-tree type并创建一个基本上实现STL映射接口的包装类(也依赖于Python中对的方便排序(两个元素元组)) )。这样的元组基本上对应于std :: map :: value_type。

是的,有Python的bisect模块,虽然在插入时是对数的,但是在插入时自平衡二进制树是对数的(对吧?),坦率地说我只想要一个对象。被称为OrderedDict或其他东西(不,Python 3.1 OrderedDict没有资格 - 这是'插入时'的排序 - 坦率地说,插入时间排序与排序有什么关系并不是很明显。)

请注意,按键排序的字典在许多行业中非常有用(例如,在财务方面,跟踪数据的价格表是很平常的,这些数据基本上是订购的价格词典 - >数量,汇总订单信息等等)。

如果有人有任何其他想法,那就太好了。我所知道的是Alex Martelli在这里的“答案”让我只有500万倍的智慧。所以我想我会问。

7 个答案:

答案 0 :(得分:2)

我有完全相同的需求,Alex Martelli的回答完全让我信服:最好是保留字典和部分排序的键列表,然后在需要时排序。这是有效的,因为python的排序算法(AKA Timsort)非常特殊。 Key-ordered dict in Python

我测试了他的实现和我的,他是最好的(因为他没有插入列表中间)

(我强烈建议你阅读AM关于timsort的评论中链接的论文,这是一颗珍珠)。

答案 1 :(得分:1)

列表是树的悲惨替代品。

插入需要移动整个列表以腾出空间;删除需要将列表向下移动。在可能的情况下批量添加或删除东西是正常的,但通常不会,或者采取非自然的扭曲来安排它。树的基本属性是插入和删除是O(log n);任何数量的handwaving都不会将O(n)转换为O(log n)。

当您已经知道它将去往何处时,将项目插入树中是O(1)。等效地,基于其节点从树中删除项目也是O(1)。 std :: map支持这两种方式。这些都是带有列表的O(n)。

树的另一个基本属性是迭代一系列值是每次迭代O(1)。组合列表和字典会失去这个,因为每次迭代都需要进行字典查找。 (元组列表方法没有这个问题。)

树是最基本的数据类型之一。 Python缺少树容器类型是一个疣。也许有一个第三方库实现了一个(例如,由“未知”先生链接的那个,我没有尝试过,所以我不能担保),但是没有标准的Python类型。

答案 2 :(得分:1)

我偶然发现了这个需要OrderedMap的问题,我惊恐地发现接受的答案是完全垃圾。所以我自己动手,万一有人发现它有用:

from bisect import *

class OrderedMap:
    """Much less efficient than a dict, 
    but keys don't need to be hashable."""
    __default_arg = object()
    def __init__(self, keyvalues_iter = None):
        self.clear()
        if keyvalues_iter is not None:
            self.update(keyvalues_iter)
    def clear(self):
        self.__keys = []
        self.__values = []
    def __index(self, key):
        if self.__keys:
            index = bisect(self.__keys, key)-1
            if self.__keys[index] == key:
                return index
        raise KeyError(key)
    def __len__(self):
        return len(self.__keys)
    def __contains__(self, key):
        try:
            self.__index(key)
            return True
        except KeyError:
            return False
    def __getitem__(self, key):
        index = self.__index(key)
        return self.__values[index]
    def __setitem__(self, key, value):
        try:
            index = self.__index(key)
            # exists
            self.__values[index] = value
        except KeyError:
            # new
            index = bisect(self.__keys, key)
            self.__keys.insert(index, key)
            self.__values.insert(index, value)
    def __delitem__(self, key):
        index = self.__index(key)
        self.__keys.pop(index)
        self.__values.pop(index)
    def __iter__(self):
        return iter(self.__keys)
    def get(self, key, default=__default_arg):
        try:
            return self[key]
        except KeyError:
            if default != OrderedMap.__default_arg:
                return default
            raise
    def setdefault(self, key, default = None):
        try:
            return self[key]
        except KeyError:
            if default != OrderedMap.__default_arg:
                self[key] = default
                return default
            raise
    def items(self):
        return zip(self.__keys, self.__values)
    def iteritems(self):
        return iter((self.__keys[x], self.__values[x])
                    for x in xrange(len(self)))
    def keys(self):
        return self.__keys[:]
    def iterkeys(self):
        return iter(self.__keys)
    def values(self):
        return self.__values[:]
    def itervalues(self):
        return iter(self.__values)
    def update(self, other):
        for k, v in other.iteritems():
            self[k] = v
    def __repr__(self):
        s = ", ".join("%s: %s" % (repr(self.__keys[x]),
                                  repr(self.__values[x]))
                      for x in xrange(len(self)))
        return "OrderedMap{%s}" % (s,)

答案 3 :(得分:1)

您可能正在寻找SortedCollection: http://code.activestate.com/recipes/577197-sortedcollection/

答案 4 :(得分:0)

对于保持排序的列表,您可以尝试模块heapq。

答案 5 :(得分:0)

Python sortedcontainers模块提供了SortedDict数据类型,正是出于这些目的。它使用修改后的B树类型数据结构,并使用纯Python编写。该模块具有100%的测试覆盖率和数小时的压力。虽然是纯Python,但它比C实现更快,并且有performance comparison来支持它。

因为它是纯Python,所以使用pip轻松安装:

pip install sortedcontainers

然后你只需:

from sortedcontainers import SortedDict
help(SortedDict)

performance comparison包含一个非常全面的替代实现列表。值得一看。

答案 6 :(得分:-1)

正如您所说,您可以使用bisect滚动您自己的实现:

class OrderedDict:
  def __init__(self, keyvalues_iter):
    self.__srtlst__ = sorted(keyvalues_iter)
  def __len__(self):
    return len(self.__srtlst__)
  def __contains__(self, key):
    index = bisect(self.__srtlst__, key)
    if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
      return True
    else:
      return False    
  def __getitem__(self, key):
    index = bisect(self.__srtlst__, key)
    if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
      return self.__srtlst__[index][1]
    else:
      raise KeyError(key)
  def __setitem__(sekf, key, value):
    index = bisect(self.__srtlst__, key)
    if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
      self.__srtlst__[index][1] = value
    else:
      self.__srtlst__[index]=(key, value)
  def __delitem__(sekf, key, value):
    index = bisect(self.__srtlst__, key)
    if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
      del __srtlst__[index]
    else:
      raise KeyError(key)
   def __iter__(self):
     return (v for k,v in self.__srtlst__)
   def clear(self):
     self.__srtlst__ = []
   def get(self, key, default=None):
    index = bisect(self.__srtlst__, key)
    if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
      return self.__srtlst__[index][1]
    else:
      return default
   def items(self):
     return self.__srtlst__[:]
  def iteritems(self):
    return iter(self.__srtlst__)
  def iterkeys(self):
    return (k for k,v in self.__srtlst__)
  def itervalues(self):
    return (v for k,v in self.__srtlst__)
  def keys(self):
    return [k for k,v in self.__srtlst__]
  def values(self):
    return [v for k,v in self.__srtlst__]
  def setdefault(self, key, default):
    index = bisect(self.__srtlst__, key)
    if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
      return self.__srtlst__[index][1]
    else:
      self.__srtlst__[index]=(key, default)
      return default
  def update(self, other):
    #a more efficient implementation could be done merging the sorted lists
    for k, v in other.iteritems():
      self[k] = v  
嗯......好像我已经为你做了那个,哦!哦!