2010-11-16 53 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函數「包裝」,重新設置標誌,然後鏈接到正常的字典方法來實際執行操作。將它們安裝到類中只需要將方法的名稱放在兩個列表中的一箇中,然後在創建類後立即將它們傳遞給輔助工具。