2013-03-09 12 views
6

मैं डेटाफाइल का प्रतिनिधित्व करने के लिए ऑर्डर्ड डिक्ट (सीपीथन, 2.7.3) उप-वर्ग कर रहा हूं। __getitem__ डेटाफ़ाइल से बाहर एक फ़ील्ड खींचता है और इसे नीचे दिए गए कोड के समान वर्तमान उदाहरण पर सेट करता है। अब __contains__ को True वापस करने के लिए ओवरराइड करना होगा यदि फ़ील्ड डिक्शनरी में है या डिस्क पर फ़ाइल में है क्योंकि इसे किसी भी तरह से पढ़ा जा सकता है। हालांकि, ऐसा लगता है कि OrderedDict की इसकी चाबियों का निरीक्षण करने की क्षमता टूट गई है।__contains__ ओवरराइडिंग क्यों OrderedDict.keys तोड़ता है?

from collections import OrderedDict 

dictclass = OrderedDict 

class Foo(dictclass): 
    def __getitem__(self,key): 
     try: 
      return dictclass.__getitem__(self,key) 
     except KeyError: 
      pass 

     data = key*2 
     self[key] = data 
     return data 

    def __contains__(self,whatever): 
     return dictclass.__contains__(self,whatever) or 'bar' in whatever 

a = Foo() 
print a['bar'] 
print a.keys() 

आप उपरोक्त कोड को चलाने, तो आप इस उत्पादन मिल जाएगा:

barbar 
[] 

ध्यान दें कि अगर आप उपरोक्त कोड में dictclass = dict बदलने के लिए, यह अभी भी काम करने के लिए लगता है (निम्न उत्पादन दे रही है) ।

barbar 
['bar'] 

क्या मैं कुछ गलत कर रहा हूं?

+0

मैं [स्रोत] (http://hg.python.org/cpython/file/2.7/Lib/collections.py) पढ़ रहा हूं और मुझे अभी भी यह पता लगाना कठिन समय है ... – mgilson

+0

मैं ऐसा कर रहा हूं और मुझे लगता है कि आपकी समस्या कहां है: '__setitem__' और' __iter__' पर एक नज़र डालें। –

+0

@ एरोडास - हाँ, यही वह जगह है जहां मैं देख रहा था। हो सकता है कि मैं बहुत थक गया हूं, लेकिन मुझे सभी तर्क सीधे रखने में मुश्किल हो रही थी। – mgilson

उत्तर

6

Foo.__contains__ परिभाषित नहीं है जब:

a['bar'] 

कॉल Foo.__getitem__, जो

self[key] = data 

यह कहता है OrderedDict.__setitem__ है, जो इस तरह से परिभाषित किया गया है कार्यान्वित:

def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 
    'od.__setitem__(i, y) <==> od[i]=y' 
    # Setting a new item creates a new link at the end of the linked list, 
    # and the inherited dictionary is updated with the new key/value pair. 
    if key not in self: 
     root = self.__root 
     last = root[PREV] 
     last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] 
    dict_setitem(self, key, value) 

012 के बाद सेपरिभाषित नहीं किया गया है,

if key not in self: 

सच है। इसलिए कुंजी को self.__root और self.__map में ठीक से जोड़ा गया है।

जब Foo.__contains__ परिभाषित किया गया है,

if key not in self: 

झूठी है। इसलिए कुंजी को self.__root और self.__map में ठीक से जोड़ा नहीं गया है। Foo.__contains__ प्रभावी मूर्ख OrderedDict.__setitem__ यह सोचने में कि 'bar' कुंजी पहले से ही जोड़ा जा चुका है।


मैं इसे उपयोगी (__setitem__ में प्रिंट बयान और __iter__ जोड़ने) निम्न कोड के साथ खेलने के लिए मिला:

from collections import OrderedDict 

dictclass = OrderedDict 

class Foo(dictclass): 
    def __getitem__(self,key): 
     try: 
      return dictclass.__getitem__(self,key) 
     except KeyError: 
      pass 

     data = key*2 
     self[key] = data 
     return data 

    def __contains__(self,whatever): 
     print('contains: {}'.format(whatever)) 
     return dictclass.__contains__(self,whatever) or 'bar' in whatever 

    def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 
     'od.__setitem__(i, y) <==> od[i]=y' 
     # Setting a new item creates a new link at the end of the linked list, 
     # and the inherited dictionary is updated with the new key/value pair. 
     print('key not in self: {}'.format(key not in self)) 
     if key not in self: 
      root = self._OrderedDict__root 
      last = root[PREV] 
      last[NEXT] = root[PREV] = self._OrderedDict__map[key] = [last, root, key] 
     dict_setitem(self, key, value) 

    def __iter__(self): 
     'od.__iter__() <==> iter(od)' 
     # Traverse the linked list in order. 
     NEXT, KEY = 1, 2 

     root = self._OrderedDict__root 
     curr = root[NEXT] 
     print('curr: {}'.format(curr)) 
     print('root: {}'.format(root)) 
     print('curr is not root: {}'.format(curr is not root)) 

     while curr is not root: 
      yield curr[KEY] 
      curr = curr[NEXT] 

a = Foo() 
print a['bar'] 
# barbar 

print a.keys() 
# ['bar'] 

सूचना है कि आप Foo का एक उपवर्ग बनाकर इस समस्या से बचने कर सकते हैं collections.MutableMapping और इसके अधिकांश व्यवहार को OrderedDict विशेषता में प्रस्तुत करना विशेषता:

import collections 
dictclass = collections.OrderedDict 

class Foo(collections.MutableMapping): 
    def __init__(self, *args, **kwargs): 
     self._data = dictclass(*args, **kwargs) 
    def __setitem__(self, key, value): 
     self._data[key] = value 
    def __delitem__(self, key): 
     del self._data[key] 
    def __iter__(self): 
     return iter(self._data) 
    def __len__(self): 
     return len(self._data) 

    def __getitem__(self,key): 
     try: 
      return self._data[key] 
     except KeyError: 
      pass 

     data = key*2 
     self[key] = data 
     return data 

    def __contains__(self,whatever): 
     return dictclass.__contains__(self,whatever) or 'bar' in whatever 

जो पैदावार

a = Foo() 
print a['bar'] 
# barbar 

print a.keys() 
# ['bar'] 
भी __contains__ परिभाषित साथ

+0

धन्यवाद। यही वह है - मैं 'स्वयं .__ रूट' पर ध्यान केंद्रित करने में बहुत अधिक समय व्यतीत कर रहा था और यह कैसे शुरू होता है - सोच रहा है - 'self .__ root = root = []; जड़ [:] = [जड़, जड़, कोई नहीं] 'क्या चल रहा है ?? !?? : एक्स – mgilson

+0

चीजों को समझने की मेरी विधि बहुत कम है - इसमें आम तौर पर बहुत सारे प्रिंट स्टेटमेंट होते हैं। :) – unutbu

2

or 'bar' in whatever आपका कोड क्या टूटता है। यदि आप इसे हटाते हैं, तो यह आपके द्वारा उल्लेख किए गए परिवर्तन dictclass = dict के साथ काम करेगा।

OrderedDict की __setitem__ कार्यान्वयन यह है:

def __setitem__(self, key, value, dict_setitem=dict.__setitem__): 
    'od.__setitem__(i, y) <==> od[i]=y' 
    # Setting a new item creates a new link at the end of the linked list, 
    # and the inherited dictionary is updated with the new key/value pair. 
    if key not in self: 
     root = self.__root 
     last = root[0] 
     last[1] = root[0] = self.__map[key] = [last, root, key] 
    return dict_setitem(self, key, value) 

तो self["bar"] = "barbar" साथ, हालत झूठी होना चाहिए, लेकिन यह भी किसी भी आइटम डालने से पहले यह सच है। इस प्रकार, कुंजी 'isn self.__root को जोड़ा गया जो OrderedDict.__iter__ में प्रयोग किया जाता है:

def __iter__(self): 
    'od.__iter__() <==> iter(od)' 
    # Traverse the linked list in order. 
    root = self.__root 
    curr = root[1]         # start at the first node 
    while curr is not root: 
     yield curr[2]        # yield the curr[KEY] 
     curr = curr[1]        # move to next node 

के बाद से मूल्यों को पुन: प्राप्त करने के लिए कोड इस iterator उपयोग करता है और self.__root"bar" शामिल नहीं है, यह ठोस कुंजी मान में वापस नहीं किया जा सकता है।

+0

हाँ, यह है। धन्यवाद। +1। बेशक, मेरे कोड में, 'जो' बार 'जो भी हो, कुछ और जटिल है जिसे मैं हटाना नहीं चाहता हूं। मुझे लगता है कि ऑर्डर्ड डिक्ट के आसपास हैकिंग इसे ठीक से काम करने के लिए बहुत मुश्किल हो रही है। मुझे लगता है कि मैं सिर्फ एक नियमित dictclass subclass और एक अलग '__order' सूची रखना होगा। – mgilson

+0

@ मिगिलसन: हो सकता है कि * बी-ए * 'ऑर्डर डिक्टर्ड 'के बजाय' फू '* है-ए *' ऑर्डरर्ड डिक्ट' हो? – unutbu

+0

@unutbu - मैं चाहता हूं कि यह एक मैपिंग प्रकार हो ताकि मैं इसे अनपैक कर सकूं ... मैं स्वयं ऑर्डर का ट्रैक रख सकता हूं। – mgilson

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