20

चूंकि OrderedDict में एक सूची (ऑर्डर किए गए तत्वों के साथ), और एक शब्दकोश (इंडेक्स के बजाए कुंजी के साथ) की विशेषताएं हैं, यह प्राकृतिक प्रतीत होता है कि आप चाबियों का उपयोग करके टुकड़ा कर सकते हैं।एक अजगर ऑर्डर्ड डिक्ट पर पूर्णांक की बजाय स्ट्रिंग कुंजियों के साथ आप कैसे टुकड़े कर सकते हैं?

>>> from collections import OrderedDict 
>>> cities = OrderedDict((('san francisco', 650), ('new york', 212), ('shanghai', 8621), ('barcelona', 42423))) 
>>> test['shanghai':] # I want all the cities from shanghai to the end of the list 
TypeError: unhashable type 

क्या इस बारे में दिलचस्प बात यह है कि यह त्रुटि OrderedDictionary.__getslice__ की वजह से लागू नहीं किया जा रहा है आप देखना चाहते हैं नहीं है। मैंने अपनी खुद की __getslice__ विधि OrderedDict पर जोड़ने की कोशिश की, लेकिन मैं इस टाइप एरर समस्या में चल रहा हूं। ऐसा लगता है जैसे पाइथन कुछ प्रकार की प्रकार की जांच कर रहा है ताकि यह लागू किया जा सके कि स्लाइस कुंजियां केवल पूर्णांक हैं, इससे पहले कि वे __getslice__ फ़ंक्शन पर भी जाएं, कितना अवांछित!

>>> class BetterOrderedDict(OrderedDict): 
     def __getslice__(self, start=None, end=None, step=1): 
      return 'potato' 

>>> test = BetterOrderedDict((('one', 1), ('two', 2), ('three', 3), ('four', 4))) 
>>> print test[1:4] 
'potato'       # ok this makes sense so far 

>>> test['one':'four'] 
TypeError: unhashable type   # WTF, strings are hashable! 

तो मेरे सवाल है, क्यों नहीं कर सकते मैं गैर पूर्णांक स्लाइस, प्रकार-चेकिंग की किस तरह भी मेरी __getslice__ समारोह तक पहुँचने से टुकड़ा कुंजी रोक रहा है को लागू किया जाता है, और मैं सी में मेरी BetterOrderedDict को लागू करने से यह ओवरराइड कर सकते हैं बाइंडिंग के साथ?

+0

आप कुंजी "चार" जब तक कुंजी "एक" से एक टुकड़ा बनाना चाहते हैं? –

+0

यूप, क्योंकि उनके पास ऑर्डर है, यह ठीक होना चाहिए। –

+0

लेकिन ... क्यों? उद्देश्य क्या है? –

उत्तर

22

__getslice__ स्लाइसिंग को लागू करने के तरीके को बहिष्कृत किया गया है। इसके बजाय आप __getitem__ साथ slice वस्तुओं संभाल चाहिए:

from collections import OrderedDict 

class SlicableDict(OrderedDict): 
    def __getitem__(self, key): 
     if isinstance(key, slice): 
      return 'potato({},{},{})'.format(key.start, key.stop, key.step) 
     return super(SlicableDict, self).__getitem__(key) 

>>> s = SlicableDict(a=1, b=2, c=3) 
>>> s 
SlicableDict([('a', 1), ('c', 3), ('b', 2)]) 
>>> s['a'] 
1 
>>> s['a':'c'] 
'potato(a,c,None)' 

और अगर आप आलू की तुलना में अधिक की जरूरत है, की तुलना में आप जिस तरह से ये तीनों टुकड़ा करने की क्रिया के संचालन को लागू कर सकते हैं:

def _key_slice_to_index_slice(items, key_slice): 
    try: 
     if key_slice.start is None: 
      start = None 
     else: 
      start = next(idx for idx, (key, value) in enumerate(items) 
         if key == key_slice.start) 
     if key_slice.stop is None: 
      stop = None 
     else: 
      stop = next(idx for idx, (key, value) in enumerate(items) 
         if key == key_slice.stop) 
    except StopIteration: 
     raise KeyError 
    return slice(start, stop, key_slice.step) 

class SlicableDict(OrderedDict): 
    def __getitem__(self, key): 
     if isinstance(key, slice): 
      items = self.items() 
      index_slice = _key_slice_to_index_slice(items, key) 
      return SlicableDict(items[index_slice]) 
     return super(SlicableDict, self).__getitem__(key) 

    def __setitem__(self, key, value): 
     if isinstance(key, slice): 
      items = self.items() 
      index_slice = _key_slice_to_index_slice(items, key) 
      items[index_slice] = value.items() 
      self.clear() 
      self.update(items) 
      return 
     return super(SlicableDict, self).__setitem__(key, value) 

    def __delitem__(self, key): 
     if isinstance(key, slice): 
      items = self.items() 
      index_slice = _key_slice_to_index_slice(items, key) 
      del items[index_slice] 
      self.clear() 
      self.update(items) 
      return 
     return super(SlicableDict, self).__delitem__(key) 
+0

बढ़िया, ठीक वही जो मैं खोज रहा था। –

4

प्रयास करें इस (बहुत बदसूरत) कार्यान्वयन

class SliceOrdered(OrderedDict): 

    def __getitem__(self, key): 
     if isinstance(key, slice): 
      tmp = OrderedDict() 
      i_self = iter(self) 
      for k in i_self: 
       if key.start <= k <= key.stop: 
        tmp[k] = self[k] 
        if key.step is not None and key.step > 1: 
         for _ in range(key.step-1): 
          try: 
           next(i_self) 
          except StopIteration: 
           break 
      return tmp 
     else: 
      return super(SliceOrdered, self).__getitem__(key) 

डेमो (Python3.4)

>>> s = SliceOrdered([('a',2), ('b',2), ('c',3), ('d',4)]) 
>>> s['a':'c'] 
OrderedDict([('a', 2), ('b', 2), ('c', 3)]) 
>>> s['a':'d':2] 
OrderedDict([('a', 2), ('c', 3)]) 

एनबी। यह शायद केवल काम करता है क्योंकि इस उदाहरण में, OrderedDict न केवल आदेश दिया गया था, बल्कि सॉर्ट किया गया था। एक अपरिवर्तित शब्दकोश में टुकड़ा 'a':'c' आवश्यक नहीं है 'b', इसलिए मेरा if key.start <= k <= key.stop तर्क संभवतः विफल रहता है। निम्नलिखित कोड का सम्मान करना चाहिए:

class SliceOrdered(OrderedDict): 
    def __getitem__(self, key): 
     if not isinstance(key, slice): 
      return super(SliceOrdered,self).__getitem__(key) 
     tmp = OrderedDict() 
     step = key.step or 1 
     accumulating = False 
     i_self = iter(self) 
     for k in i_self: 
      if k == key.start: 
       accumulating = True 
      if accumulating: 
       tmp[k] = self[k] 
       for _ in range(step-1): 
        next(i_self) 
      if k == key.stop: 
       accumulating = False 
       break 
     return tmp 
+0

धन्यवाद, मुझे नहीं पता था कि getlice के बजाय \ __ getitem__ का उपयोग किया जाता है, यह अब बहुत समझ में आता है। –

12

यह आपके द्वारा अपेक्षित स्लाइसिंग सुविधा का वास्तविक कार्यान्वयन है।

OrderedDict आंतरिक रूप से दोगुनी लिंक्ड सूची के रूप में चाबियों के क्रम को बनाए रखता है। Quoting the actual comment from Python 2.7.9,

# The internal self.__map dict maps keys to links in a doubly linked list. 
# The circular doubly linked list starts and ends with a sentinel element. 
# The sentinel element never gets deleted (this simplifies the algorithm). 
# Each link is stored as a list of length three: [PREV, NEXT, KEY]. 

अब, शब्दकोश काट करने के लिए, हम दोगुना लिंक्ड सूची, __root, जो वास्तव में एक निजी चर रहा है, name mangling mechanism द्वारा संरक्षित पुनरावृति की जरूरत है।

नोट: इसमें हैकी नाम OrderedDict के आंतरिक डेटा संरचनाओं का उपयोग करने के लिए अनमोलिंग शामिल है।

from collections import OrderedDict 

class SlicableDict(OrderedDict): 
    def __getitem__(self, key): 
     if isinstance(key, slice): 
      # Unmangle `__root` to access the doubly linked list 
      root = getattr(self, "_OrderedDict__root") 
      # By default, make `start` as the first element, `end` as the last 
      start, end = root[1][2], root[0][2] 
      start = key.start or start 
      end = key.stop or end 
      step = key.step or 1 
      curr, result, begun, counter = root[1], [], False, 0 

      # Begin iterating 
      curr, result, begun = root[1], [], False 
      while curr is not root: 
       # If the end value is reached, `break` and `return` 
       if curr[2] == end: 
        break 
       # If starting value is matched, start appending to `result` 
       if curr[2] == start: 
        begun = True 
       if begun: 
        if counter % step == 0: 
         result.append((curr[2], self[curr[2]])) 
        counter += 1 

       # Make the `curr` point to the next element 
       curr = curr[1] 

      return result 

     return super(SlicableDict, self).__getitem__(key) 

कुछ नमूना रन:

>>> s = SlicableDict(a=1, b=2, c=3, d=4) 
>>> s 
SlicableDict([('a', 1), ('c', 3), ('b', 2), ('e', 5), ('d', 4), ('f', 6)]) 
>>> s['a':'c'] 
[('a', 1)] 
>>> s['a':] 
[('a', 1), ('c', 3), ('b', 2), ('e', 5), ('d', 4)] 
>>> s[:'a'] 
[] 
>>> s['a':'f':2] 
[('a', 1), ('b', 2), ('d', 4)] 
+5

'ऑर्डर्ड डिक्ट' की "नलसाजी" को उजागर करने का शानदार जवाब। मैं उम्मीद करता हूं कि आपका उत्तर मेरी तुलना में बहुत तेज़ हो, लेकिन 'ऑर्डर्ड डिक्ट' (कार्यान्वयन नाम आदि के कारण) में कार्यान्वयन में बदलाव के आधार पर तोड़ने के अधीन रहें, जबकि "पोर्सिलीन" कार्यान्वयन का उपयोग मैंने संभवतः से सुरक्षित किया है संस्करणों के बीच टूटना लेकिन इस तरह के लगभग इतना तेज़ नहीं है। –

+0

सुपर गहन, वास्तव में इसे आलू की बजाय कार्यक्षमता को लागू करने के लिए धन्यवाद! मैंने अजगर के स्रोत में ऑर्डर्ड डिक्ट कार्यान्वयन को देखा, लेकिन इस तथ्य को याद कर रहा था कि मुझे गेटलाइस के बजाय गेटिटम का उपयोग करना चाहिए। –

+2

@thefoureye सिर्फ 'self._OrderedDict__root' के बजाय 'getattr' के साथ क्या है? – Veedrac

संबंधित मुद्दे