2010-11-16 109 views
7

的一个子集我有了很多元素的字典,我想编写一个可以返回给定指标范围元素的功能(治疗字典作为数组):蟒蛇:如何获得字典

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

如何做到这一点?

编辑:我不要求使用密钥筛选... EG)

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

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

我不在乎排序... 其实我实现服务器端分页...

+2

by __indexes__ you mean keys ??? – mouad 2010-11-16 13:08:36

+1

@singularity:看看OP的过去的问题http://stackoverflow.com/questions/4181367/python-possible-to-filter-dict你应该是对的。 – kennytm 2010-11-16 13:12:54

+0

没有,没有按键,只是经过一些排序(或根本没有排序),我想要字典的第一/最后/中间部分... – 2010-11-16 13:14:35

回答

16

编辑:词典为未订购。无论何时修改字典,都不可能使get_range返回相同的片段。如果您需要确定性结果,请替换您的dictwith 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} 
+0

不错的解决方案,仍然非常可读。 – helpermethod 2010-11-16 13:06:31

+0

调用变量the_dict或者变量的“适当”样式。 – 2010-11-16 13:09:20

+0

@Chris:好的。 (重新命名为“字典”,所以双方都很高兴:)) – kennytm 2010-11-16 13:11:36

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

或者:__begin <= key <= end__ – mouad 2010-11-16 13:06:49

+1

Eek,* please *不要调用参数'dict' ...还有'key> = begin和key <= end'会更加整洁,因为'begin <= key < =结束。酷酷的Python功能,你可以做到这一点。 – 2010-11-16 13:07:12

+0

只是按照OP ...我同意坏的变量名称。 – 2010-11-16 13:22:10

0

休息放心,你真正想要的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作为比较函数

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()方法它提供了普通的字典对象不使用内部的内容列表派生一个新类。为了减少更新(甚至创建)这个内部列表的需要,它还有一个标志,指示列表是否是最新的,并且只在必要时检查它并重建列表。

为了维护标志,每个继承的字典方法可能会改变(或改变)字典的内容,用helper函数“包装”,重新设置标志,然后链接到正常的字典方法来实际执行操作。将它们安装到类中只需要将方法的名称放在两个列表中的一个中,然后在创建类后立即将它们传递给辅助工具。