मैं "परिपूर्ण" को यथासंभव यथासंभव उपवर्ग कैसे बना सकता हूं?
अंतिम लक्ष्य एक साधारण तानाशाही है जिसमें कुंजियाँ कम होती हैं।
अगर मैं ओवरराइड करता __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)
, लेकिन इसे लागू करने के लिए अधिक जटिलता है।
कौन सा अधिक सही है? जो आपकी सही परिभाषा पर निर्भर करता है।