python:如何获取dict的子集

时间:2010-11-16 13:00:00

标签: python

我有一个包含许多元素的dict,我想编写一个可以返回给定索引范围内的元素的函数(将dict视为数组):

get_range(dict, begin, end):
    return {a new dict for all the indexes between begin and end}

如何做到这一点?

编辑:我不是要求使用密钥过滤器......例如)

{"a":"b", "c":"d", "e":"f"}

get_range(dict, 0, 1) returns {"a":"b", "c":"d"} (the first 2 elements)

我不在乎排序...... 实际上我正在实现服务器端分页...

4 个答案:

答案 0 :(得分:16)

修改未订购字典。每当修改字典时,都不可能使get_range返回相同的切片。如果您需要确定性结果,请替换dict with a collections.OrderedDict

无论如何,你可以获得切片using itertools.islice

import itertools
def get_range(dictionary, begin, end):
  return dict(itertools.islice(dictionary.iteritems(), begin, end+1)) 

按键过滤的上一个答案保留在下面:

使用@ Douglas'算法,我们可以使用生成器表达式来简化它:

def get_range(dictionary, begin, end):
  return dict((k, v) for k, v in dictionary.iteritems() if begin <= k <= end)

顺便说一句,不要使用dict作为变量名,正如您在此处所见dict是字典的构造函数。

如果您使用的是Python 3.x,则可以直接使用字典理解。

def get_range(dictionary, begin, end):
  return {k: v for k, v in dictionary.items() if begin <= k <= end}

答案 1 :(得分:3)

直接实施:

def get_range(d, begin, end):
    result = {}
    for (key,value) in d.iteritems():
        if key >= begin and key <= end:
            result[key] = value
    return result

一行:

def get_range2(d, begin, end):
    return dict([ (k,v) for (k,v) in d.iteritems() if k >= begin and k <= end ])

答案 2 :(得分:1)

请确保您真正想要的是OrderedDict,您还可以使用enumerate

#!/usr/bin/env python
def get_range(d, begin, end):
    return dict(e for i, e in enumerate(d.items()) if begin <= i <= end)

if __name__ == '__main__':
    print get_range({"a":"b", "c":"d", "e":"f"}, 0, 1)

输出:

{'a': 'b', 'c': 'd'}

ps:我允许您使用0, 1作为范围值,但您应该使用0, 2来签署“前两个元素”(并使用begin <= i < end作为比较函数

答案 3 :(得分:0)

正如其他人所提到的,在Python中,词典本质上是无序的。但是,在任何给定时刻,可以使用其keys()items()方法获取其当前键或键值对的列表。

使用这些列表的一个潜在问题是,如果字典自上次使用以来已被修改(或变异),那么它们的内容以及返回的顺序可能会有所不同。这意味着您通常无法存储和重复使用该列表,除非您每次更改字典时都更新它,以防万一您需要它。

为了使这种方法更易于管理,您可以将字典和辅助列表组合到一个新的派生类中,该类负责两者之间的同步,并提供使用列表当前内容的get_range()方法。下面是示例代码,说明如何完成此操作。它基于我从this ActiveState Python Recipe中的代码中获得的想法。

class dict_with_get_range(dict):
    def __init__(self, *args, **kwrds):
        dict.__init__(self, *args, **kwrds)
        self._list_ok = False

    def _rebuild_list(self):
        self._list = []
        for k,v in self.iteritems():
            self._list.append((k,v))
        self._list_ok = True

    def get_range(self, begin, end):
        if not self._list_ok:
            self._rebuild_list()
        return dict(self._list[i] for i in range(begin,end+1))

def _wrapMutatorMethod(methodname):
    _method = getattr(dict, methodname)
    def wrapper(self, *args, **kwrds):
        # Reset 'list OK' flag, then delegate to the real mutator method
        self._list_ok = False
        return _method(self, *args, **kwrds)
    setattr(dict_with_get_range, methodname, wrapper)

for methodname in 'delitem setitem'.split():
    _wrapMutatorMethod('__%s__' % methodname)
for methodname in 'clear update setdefault pop popitem'.split():
    _wrapMutatorMethod(methodname)
del _wrapMutatorMethod  # no longer needed

dct = dict_with_get_range({"a":"b", "c":"d", "e":"f"})
print dct.get_range(0, 1)
# {'a': 'b', 'c': 'd'}
del dct["c"]
print dct.get_range(0, 1)
# {'a': 'b', 'e': 'f'}

基本思想是从dict派生一个新类,该类还有一个内部内容列表供它提供的常规字典对象不提供的新get_range()方法使用。为了最小化更新(甚至创建)此内部列表的需要,它还有一个标志,指示列表是否是最新的,并且只检查它并在必要时重建列表。

为了维护该标志,每个继承的字典方法可能会更改(或改变)字典的内容,并使用辅助函数“包装”,重置标志,然后链接到普通字典方法以实际执行操作。将它们安装到类中只需将方法的名称放在两个列表中的一个列表中,然后在创建类之后立即将它们一次传递给辅助实用程序。