कैसे "पूरी तरह से" एक तानाशाही को खत्म करने के लिए?


218

मैं कैसे "सही" का एक उपवर्ग के रूप में कर सकते हैं dict संभव के रूप में? अंतिम लक्ष्य एक साधारण तानाशाही है जिसमें कुंजियाँ कम होती हैं।

ऐसा लगता है कि कुछ छोटे प्राइमेट का सेट होना चाहिए जिन्हें मैं इस काम के लिए ओवरराइड कर सकता हूं, लेकिन मेरे सभी शोधों और प्रयासों के अनुसार ऐसा लगता है कि ऐसा नहीं है:

  • यदि मैं ओवरराइड करता __getitem__/ करती__setitem__ हूं , तो get/ setकाम नहीं करती। मैं उन्हें कैसे काम कर सकता हूं? निश्चित रूप से मुझे उन्हें व्यक्तिगत रूप से लागू करने की आवश्यकता नहीं है?

  • क्या मैं अचार को काम करने से रोक रहा हूं, और क्या मुझे इसे लागू करने की आवश्यकता है __setstate__आदि?

  • क्या मुझे चाहिए repr, updateऔर__init__ ?

  • क्या मुझे केवल उत्परिवर्तजन का उपयोग करना चाहिए (ऐसा लगता है कि किसी को उपयोग नहीं करना चाहिए UserDict या DictMixin)? यदि हां, तो कैसे? डॉक्स बिल्कुल ज्ञानवर्धक नहीं हैं।

यहाँ मेरा पहला get()काम है , इसमें काम नहीं करना और इसमें कोई संदेह नहीं है कि कई अन्य छोटी समस्याएं हैं:

class arbitrary_dict(dict):
    """A dictionary that applies an arbitrary key-altering function
       before accessing the keys."""

    def __keytransform__(self, key):
        return key

    # Overridden methods. List from 
    # /programming/2390827/how-to-properly-subclass-dict

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    # Note: I'm using dict directly, since super(dict, self) doesn't work.
    # I'm not sure why, perhaps dict is not a new-style class.

    def __getitem__(self, key):
        return dict.__getitem__(self, self.__keytransform__(key))

    def __setitem__(self, key, value):
        return dict.__setitem__(self, self.__keytransform__(key), value)

    def __delitem__(self, key):
        return dict.__delitem__(self, self.__keytransform__(key))

    def __contains__(self, key):
        return dict.__contains__(self, self.__keytransform__(key))


class lcdict(arbitrary_dict):
    def __keytransform__(self, key):
        return str(key).lower()

मुझे लगता है कि __keytransform __ () स्थिर होना चाहिए। हालांकि अच्छा दृष्टिकोण। (पहले से
बताए @staticmethod

जवाबों:


229

आप एक ऑब्जेक्ट लिख सकते हैं जो मॉड्यूल से एबीसी एस (एब्सट्रैक्ट बेस क्लासेस) के dictसाथ काफी आसानी से व्यवहार करता है । यहां तक ​​कि यह भी बताता है कि क्या आप एक विधि से चूक गए हैं, इसलिए नीचे न्यूनतम संस्करण है जो एबीसी को बंद कर देता है।collections.abc

from collections.abc import MutableMapping


class TransformedDict(MutableMapping):
    """A dictionary that applies an arbitrary key-altering
       function before accessing the keys"""

    def __init__(self, *args, **kwargs):
        self.store = dict()
        self.update(dict(*args, **kwargs))  # use the free update to set keys

    def __getitem__(self, key):
        return self.store[self.__keytransform__(key)]

    def __setitem__(self, key, value):
        self.store[self.__keytransform__(key)] = value

    def __delitem__(self, key):
        del self.store[self.__keytransform__(key)]

    def __iter__(self):
        return iter(self.store)

    def __len__(self):
        return len(self.store)

    def __keytransform__(self, key):
        return key

आपको एबीसी से कुछ मुफ्त तरीके मिलते हैं:

class MyTransformedDict(TransformedDict):

    def __keytransform__(self, key):
        return key.lower()


s = MyTransformedDict([('Test', 'test')])

assert s.get('TEST') is s['test']   # free get
assert 'TeSt' in s                  # free __contains__
                                    # free setdefault, __eq__, and so on

import pickle
# works too since we just use a normal dict
assert pickle.loads(pickle.dumps(s)) == s

मैं dictसीधे उपवर्ग (या अन्य भवन) नहीं बनाऊंगा। यह अक्सर कोई मतलब नहीं है, क्योंकि आप वास्तव में क्या करना चाहते हैं एक के इंटरफ़ेसdict को लागू करना है । और यह वही है जो एबीसी के लिए है।


46
मैं नाम बदलने का सुझाव दूंगा __keytransform__()क्योंकि यह PEP 8 शैली गाइड का उल्लंघन करता है जो "कभी भी ऐसे नामों का आविष्कार नहीं करने की सलाह देता है; केवल वर्णनात्मक के रूप में" उन्हें दस्तावेज के रूप में उपयोग करें : नामकरण शैलियाँ अनुभाग।
मार्टिउ

1
प्रश्न हालांकि - इस इंटरफ़ेस को एक उपयोगकर्ता-परिभाषित प्रकार के साथ लागू नहीं करेगा, जिसके परिणामस्वरूप आमतौर पर धीमी गति से चलने वाले ऑपरेशन होते हैं जो अंतर्निहित प्रकार का उपयोग करते हैं?
प्रातः

2
क्या ऐसा करने का कोई तरीका है ताकि आइंस्टीन (_, तानाशाह) == सही हो? या फिर आप केवल उप-निर्माण के लिए म्यूटेबल मैपिंग का उपयोग करते हैं?
एंडी हेडन

5
@AndyHayden: आपको लिखना चाहिए if isinstance(t, collections.MutableMapping): print t, "can be used like a dict"। ऑब्जेक्ट के प्रकार की जांच न करें, इंटरफ़ेस की जांच करें।
जोचन रिट्जेल

2
@ नील इस दुर्भाग्य से अजगर मानक पुस्तकालय में जेएसओएनएनकोडर - github.com/python-git/python/blob/…
एंडी स्मिथ

97

मैं "परिपूर्ण" को यथासंभव यथासंभव उपवर्ग कैसे बना सकता हूं?

अंतिम लक्ष्य एक साधारण तानाशाही है जिसमें कुंजियाँ कम होती हैं।

  • अगर मैं ओवरराइड करता __getitem__/ करती हूं __setitem__, तो काम नहीं कर पाती / सेट होती है। मैं उन्हें कैसे काम करूँ? निश्चित रूप से मुझे उन्हें व्यक्तिगत रूप से लागू करने की आवश्यकता नहीं है?

  • क्या मैं अचार को काम करने से रोक रहा हूं, और क्या मुझे इसे लागू करने की आवश्यकता है __setstate__आदि?

  • क्या मुझे repr, update और __init__?

  • क्या मुझे बस उपयोग करना चाहिए mutablemapping(ऐसा लगता है कि एक का उपयोग नहीं करना चाहिए UserDict या DictMixin)? यदि हां, तो कैसे? डॉक्स बिल्कुल ज्ञानवर्धक नहीं हैं।

स्वीकृत उत्तर मेरा पहला दृष्टिकोण होगा, लेकिन चूंकि इसमें कुछ मुद्दे हैं, और चूंकि किसी ने विकल्प को संबोधित नहीं किया है, वास्तव में एक उपवर्ग dict , मैं यहाँ ऐसा करने जा रहा हूं।

स्वीकृत उत्तर में क्या गलत है?

यह मेरे लिए एक सरल अनुरोध की तरह लगता है:

मैं "परिपूर्ण" को यथासंभव यथासंभव उपवर्ग कैसे बना सकता हूं? अंतिम लक्ष्य एक साधारण तानाशाही है जिसमें कुंजियाँ कम होती हैं।

स्वीकृत उत्तर वास्तव में उपवर्ग नहीं है dict, और इसके लिए एक परीक्षण विफल रहता है:

>>> isinstance(MyTransformedDict([('Test', 'test')]), dict)
False

आदर्श रूप से, किसी भी प्रकार की जाँच करने वाला कोड उस इंटरफ़ेस के लिए परीक्षण करेगा जिसकी हम अपेक्षा करते हैं, या एक सार आधार वर्ग है, लेकिन अगर हमारे डेटा ऑब्जेक्ट्स को उन कार्यों में पारित किया जा रहा है जो dict- परीक्षण कर रहे हैं और हम उन कार्यों को "ठीक" नहीं कर सकते हैं, तो यह कोड असफल हो जायेगी।

अन्य क्विबल्स एक बना सकते हैं:

  • स्वीकार किए जाते हैं जवाब भी classmethod याद आ रही है: fromkeys
  • स्वीकृत उत्तर में एक निरर्थक भी है __dict__- इसलिए स्मृति में अधिक स्थान ले रहा है:

    >>> s.foo = 'bar'
    >>> s.__dict__
    {'foo': 'bar', 'store': {'test': 'test'}}

वास्तव में उपवर्ग dict

हम विरासत के माध्यम से तानाशाही तरीकों का पुन: उपयोग कर सकते हैं। हमें केवल एक इंटरफ़ेस परत बनाने की ज़रूरत है जो सुनिश्चित करती है कि चाबी को लोअरकेस रूप में हुक में पारित किया जाए यदि वे तार हैं।

अगर मैं ओवरराइड करता __getitem__/ करती हूं __setitem__, तो काम नहीं कर पाती / सेट होती है। मैं उन्हें कैसे काम करूँ? निश्चित रूप से मुझे उन्हें व्यक्तिगत रूप से लागू करने की आवश्यकता नहीं है?

ठीक है, उन्हें प्रत्येक व्यक्ति को व्यक्तिगत रूप से लागू करना इस दृष्टिकोण और उपयोग करने के लिए उल्टा है MutableMapping(स्वीकृत उत्तर देखें), लेकिन यह वास्तव में इतना अधिक काम नहीं है।

पहले, चलो पायथन 2 और 3 के बीच के अंतर को स्पष्ट करते हैं, _RaiseKeyErrorयह सुनिश्चित करने के लिए कि हम वास्तव में एक तर्क प्राप्त करते हैं dict.pop, और हमारी स्ट्रिंग कुंजियों को कम करने के लिए फ़ंक्शन बनाने के लिए एक सिंगलटन ( ) बनाएं।

from itertools import chain
try:              # Python 2
    str_base = basestring
    items = 'iteritems'
except NameError: # Python 3
    str_base = str, bytes, bytearray
    items = 'items'

_RaiseKeyError = object() # singleton for no-default behavior

def ensure_lower(maybe_str):
    """dict keys can be any hashable object - only call lower if str"""
    return maybe_str.lower() if isinstance(maybe_str, str_base) else maybe_str

अब हम लागू करते हैं - मैं superपूर्ण तर्कों के साथ उपयोग कर रहा हूं ताकि यह कोड पायथन 2 और 3 के लिए काम करे:

class LowerDict(dict):  # dicts take a mapping or iterable as their optional first argument
    __slots__ = () # no __dict__ - that would be redundant
    @staticmethod # because this doesn't make sense as a global function.
    def _process_args(mapping=(), **kwargs):
        if hasattr(mapping, items):
            mapping = getattr(mapping, items)()
        return ((ensure_lower(k), v) for k, v in chain(mapping, getattr(kwargs, items)()))
    def __init__(self, mapping=(), **kwargs):
        super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
    def __getitem__(self, k):
        return super(LowerDict, self).__getitem__(ensure_lower(k))
    def __setitem__(self, k, v):
        return super(LowerDict, self).__setitem__(ensure_lower(k), v)
    def __delitem__(self, k):
        return super(LowerDict, self).__delitem__(ensure_lower(k))
    def get(self, k, default=None):
        return super(LowerDict, self).get(ensure_lower(k), default)
    def setdefault(self, k, default=None):
        return super(LowerDict, self).setdefault(ensure_lower(k), default)
    def pop(self, k, v=_RaiseKeyError):
        if v is _RaiseKeyError:
            return super(LowerDict, self).pop(ensure_lower(k))
        return super(LowerDict, self).pop(ensure_lower(k), v)
    def update(self, mapping=(), **kwargs):
        super(LowerDict, self).update(self._process_args(mapping, **kwargs))
    def __contains__(self, k):
        return super(LowerDict, self).__contains__(ensure_lower(k))
    def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
        return type(self)(self)
    @classmethod
    def fromkeys(cls, keys, v=None):
        return super(LowerDict, cls).fromkeys((ensure_lower(k) for k in keys), v)
    def __repr__(self):
        return '{0}({1})'.format(type(self).__name__, super(LowerDict, self).__repr__())

हम किसी भी विधि या विशेष विधि के लिए लगभग एक बॉयलर-प्लेट दृष्टिकोण का उपयोग कि संदर्भ में एक महत्वपूर्ण, लेकिन अन्यथा, विरासत से, हम पाते हैं तरीके: len, clear, items, keys, popitem, और valuesमुक्त करने के लिए। जबकि इसके लिए कुछ सावधानी बरतने की आवश्यकता होती है, यह देखना सही है कि यह काम करता है।

(ध्यान दें कि haskeyअजगर 2 में हटा दिया गया था, अजगर 3 में हटा दिया गया था)

यहाँ कुछ उपयोग है:

>>> ld = LowerDict(dict(foo='bar'))
>>> ld['FOO']
'bar'
>>> ld['foo']
'bar'
>>> ld.pop('FoO')
'bar'
>>> ld.setdefault('Foo')
>>> ld
{'foo': None}
>>> ld.get('Bar')
>>> ld.setdefault('Bar')
>>> ld
{'bar': None, 'foo': None}
>>> ld.popitem()
('bar', None)

क्या मैं अचार को काम करने से रोक रहा हूं, और क्या मुझे इसे लागू करने की आवश्यकता है __setstate__आदि?

नमकीन बनाना

और तानाशाही उपवर्ग अचार ठीक है:

>>> import pickle
>>> pickle.dumps(ld)
b'\x80\x03c__main__\nLowerDict\nq\x00)\x81q\x01X\x03\x00\x00\x00fooq\x02Ns.'
>>> pickle.loads(pickle.dumps(ld))
{'foo': None}
>>> type(pickle.loads(pickle.dumps(ld)))
<class '__main__.LowerDict'>

__repr__

क्या मुझे repr, update और __init__?

हमने परिभाषित किया updateऔर __init__, लेकिन आपके पास __repr__डिफ़ॉल्ट रूप से एक सुंदर है:

>>> ld # without __repr__ defined for the class, we get this
{'foo': None}

हालाँकि, __repr__अपने कोड की डिबगैबिलिटी को बेहतर बनाने के लिए लिखना अच्छा है । आदर्श परीक्षा है eval(repr(obj)) == obj। यदि आपके कोड के लिए यह करना आसान है, तो मैं दृढ़ता से इसकी अनुशंसा करता हूं:

>>> ld = LowerDict({})
>>> eval(repr(ld)) == ld
True
>>> ld = LowerDict(dict(a=1, b=2, c=3))
>>> eval(repr(ld)) == ld
True

आप देखते हैं, यह वास्तव में एक समान वस्तु को फिर से बनाने की आवश्यकता है - यह एक ऐसी चीज है जो हमारे लॉग या बैकट्रैक में दिखाई दे सकती है:

>>> ld
LowerDict({'a': 1, 'c': 3, 'b': 2})

निष्कर्ष

क्या मुझे बस उपयोग करना चाहिए mutablemapping(ऐसा लगता है कि एक का उपयोग नहीं करना चाहिए UserDict या DictMixin)? यदि हां, तो कैसे? डॉक्स बिल्कुल ज्ञानवर्धक नहीं हैं।

हाँ, ये कोड की कुछ और पंक्तियाँ हैं, लेकिन वे व्यापक होने का इरादा रखते हैं। मेरा पहला झुकाव स्वीकार किए गए उत्तर का उपयोग करना होगा, और अगर इसके साथ कोई समस्या थी, तो मैं अपने उत्तर को देखूंगा - क्योंकि यह थोड़ा अधिक जटिल है, और मुझे अपना इंटरफ़ेस सही करने में मदद करने के लिए कोई एबीसी नहीं है।

समय से पहले अनुकूलन प्रदर्शन की तलाश में अधिक जटिलता के लिए जा रहा है। MutableMappingसरल है - इसलिए इसे एक तत्काल बढ़त मिलती है, बाकी सभी समान हैं। फिर भी, सभी मतभेदों को दूर करने के लिए, आइए तुलना करें और इसके विपरीत करें।

मुझे जोड़ना चाहिए कि collectionsमॉड्यूल में एक समान शब्दकोश डालने के लिए एक धक्का था , लेकिन इसे अस्वीकार कर दिया गया था । आपको इसके बजाय शायद ऐसा करना चाहिए:

my_dict[transform(key)]

यह कहीं अधिक आसानी से बहस योग्य होना चाहिए।

तुलना और इसके विपरीत

MutableMapping(जो गुम है fromkeys) और 11 dictउपवर्ग के साथ कार्यान्वित 6 इंटरफ़ेस फ़ंक्शन हैं । मैं लागू करने के लिए की जरूरत नहीं है __iter__या __len__, लेकिन इसके बजाय मैं लागू करने के लिए है get, setdefault, pop, update, copy, __contains__, औरfromkeys के बाद से मैं उन कार्यान्वयन के अधिकांश के लिए विरासत का उपयोग कर सकते हैं, लेकिन इन काफी तुच्छ कर रहे हैं -।

MutableMappingऔजार अजगर में कुछ चीजें हैं जो dictसी में लागू - तो मैं एक उम्मीद होती है dictउपवर्ग कुछ मामलों में अधिक performant किया जाना है।

हम __eq__दोनों दृष्टिकोणों में एक मुक्त हो जाते हैं - दोनों ही समानता का अनुमान लगाते हैं यदि एक और तानाशाही सभी को कम करती है - लेकिन फिर, मुझे लगता है कि dictउपवर्ग अधिक तेज़ी से तुलना करेगा।

सारांश:

  • उप-वर्ग MutableMappingबग के लिए कम अवसरों के साथ सरल है, लेकिन धीमी गति से, अधिक मेमोरी लेता है (अनावश्यक तानाशाही देखें), और विफल हो जाता हैisinstance(x, dict)
  • उपवर्ग dictतेज है, कम मेमोरी का उपयोग करता है, और गुजरता है isinstance(x, dict), लेकिन इसे लागू करने के लिए अधिक जटिलता है।

कौन सा अधिक सही है? जो आपकी सही परिभाषा पर निर्भर करता है।


स्वीकार किए गए उत्तर को निरर्थक तानाशाही कैसे दूर होगी?
सीनि123

1
तुरंत ध्यान में आने वाले दो तरीके या तो स्टोर की विशेषता को घोषित करते हैं __slots__या शायद __dict__स्टोर के रूप में पुन: उपयोग करते हैं, लेकिन यह शब्दार्थवाद की आलोचना का एक और संभावित बिंदु है।
हारून हॉल

1
क्या यह एक डेकोरेटर लिखना आसान होगा जो एक विधि लेता है और आपके ensure_lowerपहले बहस (जो हमेशा कुंजी है) पर उपयोग करता है? फिर यह ओवरराइड की समान संख्या होगी, लेकिन वे सभी फॉर्म के होंगे __getitem__ = ensure_lower_decorator(super(LowerDict, self).__getitem__)
ग्राईफ

1
इसके लिए धन्यवाद - पॉप और डेकीज़ के लिए चेतावनी प्राप्त करना कि वे आधार वर्ग विधि के हस्ताक्षर से मेल नहीं खाते हैं।
Mr_and_Mrs_D

1
@Mr_and_Mrs_D मैंने एक कार्यान्वयन जोड़ा copy- मुझे लगता है कि इसे करना चाहिए, नहीं? मुझे लगता है कि इसे इंटरफ़ेस के लिए परीक्षण करना चाहिए - उदाहरण के लिए पांडा डेटाफ़्रेम ऑब्जेक्ट मैपिंग इंस्टेंस नहीं है (अंतिम जांच में) लेकिन इसमें आइटम / पुनरावृत्तियाँ हैं।
एरॉन हॉल

4

मेरी आवश्यकताएं थोड़ी सख्त थीं:

  • मुझे केस की जानकारी को बनाए रखना था (स्ट्रिंग उपयोगकर्ता के लिए प्रदर्शित फ़ाइलों के लिए पथ हैं, लेकिन यह एक विंडोज़ ऐप है, इसलिए आंतरिक रूप से सभी कार्यों को असंवेदनशील होना चाहिए)
  • मुझे यथासंभव छोटे होने की कुंजी की आवश्यकता थी (यह स्मृति प्रदर्शन में फर्क करता था , 370 में से 110 एमबी काट दिया गया)। इसका मतलब यह था कि चाबियों का लोअरकेस संस्करण कैशिंग एक विकल्प नहीं है।
  • मुझे डेटा संरचनाओं के निर्माण की आवश्यकता थी जितनी जल्दी हो सके (फिर से प्रदर्शन में अंतर बना, इस बार गति)। मुझे एक बिलिन के साथ जाना था

मेरा प्रारंभिक विचार एक असंवेदनशील यूनिकोड उपवर्ग के लिए हमारे क्लंकी पथ वर्ग को प्रतिस्थापित करना था - लेकिन:

  • उस अधिकार को पाने के लिए कड़ी मेहनत करना - देखें: अजगर में एक असंवेदनशील स्ट्रिंग क्लास
  • पता चलता है कि स्पष्ट रूप से तानाशाही से निपटने वाली स्पष्ट कुंजी कोड क्रिया और गड़बड़ कर देती है - और त्रुटि प्रवण (संरचनाएं और भी पास हो जाती हैं, और यह स्पष्ट नहीं है कि अगर उनके पास कुंजी / तत्व के रूप में CIStr उदाहरण हैं, तो भूल जाना आसान है और some_dict[CIstr(path)]बदसूरत है)

इसलिए मुझे आखिरकार उस मामले को असंवेदनशील तरीके से लिखना पड़ा। @AaronHall द्वारा कोड के लिए धन्यवाद जो 10 बार आसान बनाया गया था।

class CIstr(unicode):
    """See https://stackoverflow.com/a/43122305/281545, especially for inlines"""
    __slots__ = () # does make a difference in memory performance

    #--Hash/Compare
    def __hash__(self):
        return hash(self.lower())
    def __eq__(self, other):
        if isinstance(other, CIstr):
            return self.lower() == other.lower()
        return NotImplemented
    def __ne__(self, other):
        if isinstance(other, CIstr):
            return self.lower() != other.lower()
        return NotImplemented
    def __lt__(self, other):
        if isinstance(other, CIstr):
            return self.lower() < other.lower()
        return NotImplemented
    def __ge__(self, other):
        if isinstance(other, CIstr):
            return self.lower() >= other.lower()
        return NotImplemented
    def __gt__(self, other):
        if isinstance(other, CIstr):
            return self.lower() > other.lower()
        return NotImplemented
    def __le__(self, other):
        if isinstance(other, CIstr):
            return self.lower() <= other.lower()
        return NotImplemented
    #--repr
    def __repr__(self):
        return '{0}({1})'.format(type(self).__name__,
                                 super(CIstr, self).__repr__())

def _ci_str(maybe_str):
    """dict keys can be any hashable object - only call CIstr if str"""
    return CIstr(maybe_str) if isinstance(maybe_str, basestring) else maybe_str

class LowerDict(dict):
    """Dictionary that transforms its keys to CIstr instances.
    Adapted from: https://stackoverflow.com/a/39375731/281545
    """
    __slots__ = () # no __dict__ - that would be redundant

    @staticmethod # because this doesn't make sense as a global function.
    def _process_args(mapping=(), **kwargs):
        if hasattr(mapping, 'iteritems'):
            mapping = getattr(mapping, 'iteritems')()
        return ((_ci_str(k), v) for k, v in
                chain(mapping, getattr(kwargs, 'iteritems')()))
    def __init__(self, mapping=(), **kwargs):
        # dicts take a mapping or iterable as their optional first argument
        super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
    def __getitem__(self, k):
        return super(LowerDict, self).__getitem__(_ci_str(k))
    def __setitem__(self, k, v):
        return super(LowerDict, self).__setitem__(_ci_str(k), v)
    def __delitem__(self, k):
        return super(LowerDict, self).__delitem__(_ci_str(k))
    def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
        return type(self)(self)
    def get(self, k, default=None):
        return super(LowerDict, self).get(_ci_str(k), default)
    def setdefault(self, k, default=None):
        return super(LowerDict, self).setdefault(_ci_str(k), default)
    __no_default = object()
    def pop(self, k, v=__no_default):
        if v is LowerDict.__no_default:
            # super will raise KeyError if no default and key does not exist
            return super(LowerDict, self).pop(_ci_str(k))
        return super(LowerDict, self).pop(_ci_str(k), v)
    def update(self, mapping=(), **kwargs):
        super(LowerDict, self).update(self._process_args(mapping, **kwargs))
    def __contains__(self, k):
        return super(LowerDict, self).__contains__(_ci_str(k))
    @classmethod
    def fromkeys(cls, keys, v=None):
        return super(LowerDict, cls).fromkeys((_ci_str(k) for k in keys), v)
    def __repr__(self):
        return '{0}({1})'.format(type(self).__name__,
                                 super(LowerDict, self).__repr__())

स्पष्ट बनाम स्पष्ट होना अभी भी एक समस्या है, लेकिन एक बार धूल सुलझने के बाद, ci के साथ शुरू करने के लिए विशेषताओं / चर का नाम बदलकर (और यह कहते हुए कि एक बड़ा मोटा डॉक्टर टिप्पणी करता है कि ci असंवेदनशील है) मुझे लगता है कि एक सही समाधान है - कोड के पाठकों के लिए पूरी तरह से अवगत रहें कि हम केस असंवेदनशील अंतर्निहित डेटा संरचनाओं के साथ काम कर रहे हैं। यह उम्मीद है कि कीड़े को पुन: उत्पन्न करने के लिए कुछ मुश्किल को ठीक करेगा, जो मुझे संदेह है कि मामला संवेदनशीलता के लिए उबालता है।

टिप्पणियाँ / सुधार का स्वागत :)


CIstr को eval (repr (obj)) पास करने __repr__के __repr__लिए पैरेंट क्लास का उपयोग करना चाहिए == obj टेस्ट (मुझे नहीं लगता कि यह अभी करता है) और भरोसा नहीं करता __str__
हारून हॉल

total_orderingक्लास डेकोरेटर की भी जाँच करें - जो आपके यूनिकोड उपवर्ग से 4 विधियों को समाप्त कर देगा। लेकिन तानाशाह उपवर्ग बहुत चतुराई से लागू होता है। : पी
हारून हॉल

धन्यवाद @AaronHall - यह आपने ही लागू किया है कि: P Re: कुल आदेश - मैंने जानबूझकर इन रेमंड हेटिंगर द्वारा सलाह के रूप में इनबिल्ट किए गए तरीके यहां लिखे: stackoverflow.com/a/43122305/281545 । पुन: repr: मुझे याद है कि एक टिप्पणी (कुछ कोर देव IIRC द्वारा) अच्छी तरह से, यह वास्तव में कोशिश करने और उस परीक्षा को पास करने के लिए repr करने के लिए परेशानी के लायक नहीं है (यह एक परेशानी है) - जितना संभव हो उतना जानकारीपूर्ण होने पर बेहतर ध्यान दें ( लेकिन अधिक नहीं)
Mr_and_Mrs_D

मैं आपको अपने निरर्थक तुलना तरीकों की अनुमति दूंगा (आपको अपने उत्तर में इसके बारे में एक नोट करना चाहिए), लेकिन CIstr.__repr__, आपके मामले में, बहुत कम परेशानी के साथ रीप्र की परीक्षा उत्तीर्ण कर सकता है, और इसे डीबगिंग को बहुत अच्छा बनाना चाहिए। मैं __repr__आपके हुक्म के लिए भी जोड़ूंगा। मैं इसे अपने उत्तर में प्रदर्शित करने के लिए करूँगा।
आरोन हॉल

@AaronHall: मैंने __slots__CIstr में जोड़ा - प्रदर्शन में फर्क पड़ता है (CIstr का मतलब उपवर्ग नहीं है या वास्तव में लोअर डिडक्ट के बाहर प्रयोग किया जाता है, एक स्थिर नेस्टेड अंतिम वर्ग होना चाहिए)। अभी भी यकीन नहीं है कि कैसे पुनर्निर्भरता से समस्या हल करें (स्टिंग में 'और "उद्धरण का एक संयोजन हो सकता है )
Mr_and_Mrs_D

4

आपको बस इतना करना होगा

class BatchCollection(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(*args, **kwargs)

या

class BatchCollection(dict):
    def __init__(self, inpt={}):
        super(BatchCollection, self).__init__(inpt)

मेरे व्यक्तिगत उपयोग के लिए एक नमूना उपयोग

### EXAMPLE
class BatchCollection(dict):
    def __init__(self, inpt={}):
        dict.__init__(*args, **kwargs)

    def __setitem__(self, key, item):
        if (isinstance(key, tuple) and len(key) == 2
                and isinstance(item, collections.Iterable)):
            # self.__dict__[key] = item
            super(BatchCollection, self).__setitem__(key, item)
        else:
            raise Exception(
                "Valid key should be a tuple (database_name, table_name) "
                "and value should be iterable")

नोट : केवल अजगर 3 में परीक्षण किया गया


3

शीर्ष दो सुझावों में से दोनों को आज़माने के बाद , मैं पायथन 2.7 के लिए एक छायादार दिखने वाले मध्य मार्ग पर बस गया हूं। शायद 3 हिरण है, लेकिन मेरे लिए:

class MyDict(MutableMapping):
   # ... the few __methods__ that mutablemapping requires
   # and then this monstrosity
   @property
   def __class__(self):
       return dict

जिसे मैं वास्तव में घृणा करता हूं, लेकिन लगता है कि मेरी आवश्यकताएं हैं, जो हैं:

  • ओवरराइड कर सकते हैं **my_dict
    • यदि आपको विरासत में मिला है dict, तो यह आपके कोड को दरकिनार कर देता है । कोशिश करके देखो।
    • इस बनाता है # 2 मेरे लिए अस्वीकार्य हर समय , के रूप में यह अजगर कोड में बहुत आम है
  • के रूप में masquerades isinstance(my_dict, dict)
    • MutableMapping अकेले नियम करता है, इसलिए # 1 पर्याप्त नहीं है
    • मैं दिल से # 1 सलाह देता हूं अगर आपको इसकी आवश्यकता नहीं है, तो यह सरल और अनुमानित है
  • पूरी तरह से नियंत्रणीय व्यवहार
    • इसलिए मुझे विरासत में नहीं मिली dict

यदि आपको खुद को दूसरों से अलग बताने की आवश्यकता है, तो व्यक्तिगत रूप से मैं कुछ इस तरह का उपयोग करता हूं (हालांकि मैं बेहतर नामों की सिफारिश करूंगा):

def __am_i_me(self):
  return True

@classmethod
def __is_it_me(cls, other):
  try:
    return other.__am_i_me()
  except Exception:
    return False

जब तक आपको केवल अपने आप को आंतरिक रूप से पहचानने की आवश्यकता होती है, इस तरह __am_i_meसे अजगर के नाम-मुंगिंग के कारण गलती से कॉल करना कठिन होता है ( _MyDict__am_i_meइस वर्ग के बाहर कॉलिंग से इसका नाम बदल दिया जाता है)। _methodअभ्यास और सांस्कृतिक रूप से , दोनों की तुलना में थोड़ा अधिक निजी ।

अब तक मुझे कोई शिकायत नहीं है, गंभीरता से-छायादार __class__ओवरराइड से अलग। मैं ऐसी किसी भी समस्या के बारे में सुनकर रोमांचित हो जाऊंगा जिसका दूसरों के साथ सामना होता है, हालांकि मैं इसके परिणामों को पूरी तरह से नहीं समझता। लेकिन अभी तक मुझे कोई समस्या नहीं हुई है, और इसने मुझे बहुत सारे स्थानों में बिना किसी बदलाव की आवश्यकता के बहुत सारे स्थानों में बहुत-सी नकल-गुणवत्ता कोड स्थानांतरित करने की अनुमति दी।


साक्ष्य के रूप में: https://repl.it/repls/TraumaticToughCockatoo

मूल रूप से: वर्तमान # 2 विकल्प को कॉपी करें , print 'method_name'हर विधि में लाइनें जोड़ें और फिर इसे आज़माएं और आउटपुट देखें:

d = LowerDict()  # prints "init", or whatever your print statement said
print '------'
splatted = dict(**d)  # note that there are no prints here

आप अन्य परिदृश्यों के लिए समान व्यवहार देखेंगे। अपने नकली कहें - dictकुछ अन्य डेटाटाइप के आसपास एक रैपर है, इसलिए बैकिंग-ताना में डेटा स्टोर करने का कोई उचित तरीका नहीं है; **your_dictहर दूसरे तरीके की परवाह किए बिना, खाली हो जाएगा।

यह सही तरीके से काम करता है MutableMapping, लेकिन जैसे ही आप इससे विरासत में लेते हैं, dictयह बेकाबू हो जाता है।


संपादित करें: एक अद्यतन के रूप में, यह लगभग दो वर्षों से एक ही मुद्दे के बिना चल रहा है, कई सौ हज़ार (एह, जटिल हो सकता है, विरासत में मिली अजगर की एक जोड़ी)। तो मैं इसके साथ बहुत खुश हूँ :)

संपादित करें 2: जाहिरा तौर पर मैंने इस या कुछ समय पहले गलत नकल की। चेक के @classmethod __class__लिए काम नहीं isinstanceकरता है - @property __class__करता है: https://repl.it/repls/UnitedScientificSequence


वास्तव में क्या आप मतलब है " **your_dictखाली हो जाएगा" (यदि आप से उपवर्ग dict)? मैंने तानाशाही उतारने के साथ कोई समस्या नहीं देखी है ...
मैट पी

यदि आप वास्तव में पैरेंट डिक्टेड (जैसे लोअरडिक्ट करता है) में डेटा डालते हैं, तो यह काम करता है - आपको वह डिक्टेट-स्टोरेड डेटा मिलेगा। यदि आप नहीं कहते (आप मक्खी पर डेटा उत्पन्न करना चाहते थे, जैसे {access_count: "एक्सेस का स्टैक ट्रेस"} जो हर बार पढ़ने पर भरता है), तो आप देखेंगे कि **your_dictआपका कोड निष्पादित नहीं करता है, इसलिए कुछ भी "विशेष" आउटपुट नहीं कर सकता। उदाहरण के लिए, आप "रीड्स" की गणना नहीं कर सकते क्योंकि यह आपके रीड-काउंटिंग कोड को निष्पादित नहीं करता है। MutableMapping इसके लिए काम करता है (यदि आप कर सकते हैं तो इसका उपयोग करें!), लेकिन यह विफल रहता है isinstance(..., dict)इसलिए मैं इसका उपयोग नहीं कर सका। yay विरासत सॉफ्टवेयर।
ग्रॉक्सएक्स

ठीक है, मैं देख रहा हूं कि अब आपका क्या मतलब है। मुझे लगता है कि मुझे कोड निष्पादन की उम्मीद नहीं थी **your_dict, लेकिन मुझे लगता है कि यह बहुत दिलचस्प MutableMappingहोगा।
मैट पी

हाँ। यह कई चीजों के लिए आवश्यक है (उदाहरण के लिए, मैं RPC कॉल्स को स्थानीय-तानाशाह के लिए पढ़ी जाने वाली चीजों में ढाल रहा था, और इसे R ™ ™ की मांग पर करना था), और ऐसा लगता है कि बहुत कम लोग इसके बारे में जानते हैं, यहां तक ​​कि **some_dictकाफी सामान्य है। बहुत कम से कम यह डेकोरेटर्स में बहुत बार होता है, इसलिए यदि आपके पास कोई है , तो आपको तुरंत असंभव-सा दुर्व्यवहार होने का खतरा है यदि आप इसके लिए जिम्मेदार नहीं हैं।
ग्रॉक्सक्स

शायद मुझे कुछ याद आ रहा है, लेकिन def __class__() पायथन 2 या 3 के साथ काम करने के ट्रिक नहीं लगती है, कम से कम इस सवाल में उदाहरण कोड के लिए कि कैसे एक एबीसी के कार्यान्वयन को रजिस्टर करें। एक प्रमुख उपवर्ग के रूप में मैपिंग? (अन्यथा दो संस्करणों में काम करने के लिए संशोधित)। मैं isinstance(SpreadSheet(), dict)वापस लौटना चाहता हूं True
मार्टीन्यू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.