2010-01-09 23 views
29

यहाँ, एक दोहरा सवाल यह है कि एक सैद्धांतिक भाग के साथ, और एक व्यावहारिक एक:उपclassing dict: dict.__ init __() को बुलाया जाना चाहिए?

जब उपवर्गीकरण dict:

class ImageDB(dict): 
    def __init__(self, directory): 
     dict.__init__(self) # Necessary?? 
     ... 

चाहिए dict.__init__(self) कहा जा, बस एक "सुरक्षा" उपाय के रूप में (जैसे, मामले में कुछ गैर-मामूली कार्यान्वयन विवरण हैं जो महत्वपूर्ण हैं)? क्या dict.__init__() नामक पाइथन के भविष्य के संस्करण के साथ कोड ब्रेक है? मैं एक चीज़ या दूसरे को करने का मौलिक कारण ढूंढ रहा हूं, यहां (व्यावहारिक रूप से, dict.__init__() को सुरक्षित करना सुरक्षित है)।

मेरा अनुमान है कि जब ImageDB.__init__(self, directory) कहा जाता है, तो स्वयं पहले से ही एक नया खाली खाली वस्तु है, और इसलिए dict.__init__ पर कॉल करने की आवश्यकता नहीं है (मैं चाहता हूं कि पहले खाली होना खाली हो)। क्या ये सही है?

संपादित:

बुनियादी सवाल इसके बाद के संस्करण के पीछे और अधिक व्यावहारिक प्रश्न इस प्रकार है। मैं उप-वर्गीकरण के बारे में सोच रहा था क्योंकि मैं डीबी [...] वाक्यविन्यास का उपयोग अक्सर करता हूं (db.contents [...] हर समय करने के बजाय); ऑब्जेक्ट का एकमात्र डेटा (विशेषता) वास्तव में वास्तव में एक नियम है। मैं डेटाबेस में कुछ विधियों को जोड़ना चाहता हूं (जैसे get_image_by_name(), या get_image_by_code(), उदाहरण के लिए), और केवल __init__() ओवरराइड करें, क्योंकि छवि डेटाबेस को उस निर्देशिका द्वारा परिभाषित किया गया है जिसमें यह शामिल है।

सारांश में, (व्यावहारिक) प्रश्न हो सकता है: क्या कुछ है कि एक शब्दकोश की तरह बर्ताव करता है, सिवाय इसके कि इसके प्रारंभ अलग है के लिए एक अच्छा कार्यान्वयन है (यह केवल एक निर्देशिका का नाम लेता है), और यह अतिरिक्त है कि तरीकों?

कई उत्तरों में "कारखानों" का उल्लेख किया गया था। तो मुझे लगता है कि यह सब नीचे उबलता है: क्या आप 0 वर्गओवरराइड करते हैं और विधियों को जोड़ते हैं, या आप एक (फैक्ट्री) फ़ंक्शन लिखते हैं जो एक नियम देता है, जिसमें आप विधियां जोड़ते हैं? मैं पहला समाधान पसंद करने के इच्छुक हूं, क्योंकि फैक्ट्री फ़ंक्शन एक ऑब्जेक्ट देता है जिसका प्रकार इंगित नहीं करता है कि इसमें अतिरिक्त अर्थशास्त्र और विधियां हैं, लेकिन आपको क्या लगता है?

संपादित 2:

मैं हर किसी के जवाब से इकट्ठा कि यह एक अच्छा विचार dict उपवर्ग के लिए जब नया वर्ग "एक शब्दकोश नहीं है", और विशेष रूप से अपने __init__ विधि ही नहीं ले जा सकते हैं जब नहीं है तर्क के रूप में तर्क __init__ (जो ऊपर "व्यावहारिक प्रश्न" में मामला है)। दूसरे शब्दों में, यदि मैं सही ढंग से समझता हूं, तो सर्वसम्मति प्रतीत होती है: जब आप उप-वर्ग करते हैं, तो सभी विधियों (प्रारंभिक समेत) में बेस क्लास विधियों के समान हस्ताक्षर होना चाहिए। यह उदाहरण के लिए dict.__init__() की तरह subclass_instance.__init__() की गारंटी के लिए isinstance (subclass_instance, dict) को अनुमति देता है।

एक और व्यावहारिक प्रश्न तब पॉप अप करता है: इसकी कक्षा प्रारंभिक विधि को छोड़कर, एक वर्ग जो सिर्फ ताना की तरह है, को लागू किया जाना चाहिए? subclassing के बिना? इसके लिए कुछ परेशान बॉयलरप्लेट कोड की आवश्यकता होगी, नहीं?

+0

एक फैक्ट्री फ़ंक्शन जाने का तरीका है। यदि आपको * इंस्टेंस * व्यवहार को कस्टमाइज़ करने की आवश्यकता है, तो आप उप-वर्ग बनाना चाहते हैं। यदि आप * प्रारंभिकरण * को ओवरराइड करना चाहते हैं * आपको कुछ भी उपclassing की आवश्यकता नहीं है, क्योंकि आपके उदाहरण मानक से अलग नहीं हैं। याद रखें कि __init__ को उदाहरण के इंटरफ़ेस का हिस्सा नहीं माना जाता है, लेकिन कक्षा का। –

+0

जहां तक ​​मैं इसे इस समस्या के लिए देखता हूं, वहां एक dictgetitem__' विधि को आपके छवि डीबी में एक उप-वर्ग के बजाय जोड़ने के लिए सबसे अच्छा होगा, क्योंकि यह _not_ एक dict# है। यह आपको वही करने की अनुमति देता है जो आप चाहते हैं, _without_ उन सभी विधियों जैसे 'पॉप()' जो आपकी कक्षा के लिए अनुचित प्रतीत होता है। –

+0

@gs: अच्छा बिंदु, पॉप के बारे में; वास्तव में, कम से कम इस पल के लिए, अप्रासंगिक (डेटाबेस सामग्री केवल प्रारंभिक पर परिभाषित है)। मुझे लगता है कि यह वास्तव में सबसे अच्छा है कि कार्यान्वयन आवश्यक सुविधाओं को कसकर फिट करता है। – EOL

उत्तर

15

आपको उप-वर्गीकरण होने पर शायद dict.__init__(self) पर कॉल करना चाहिए; वास्तव में, आप नहीं जानते कि वास्तव में क्या हो रहा है (क्योंकि यह एक बिल्टिन है), और यह संस्करणों और कार्यान्वयन में भिन्न हो सकता है। इसे कॉल न करने के परिणामस्वरूप अनुचित व्यवहार हो सकता है, क्योंकि आप नहीं जानते कि कहां अपने आंतरिक डेटा संरचनाओं को पकड़ रहा है।

वैसे, आपने हमें यह नहीं बताया कि आप करना चाहते हैं; यदि आप एक कक्षा (मैपिंग) व्यवहार के साथ एक कक्षा चाहते हैं, और आपको वास्तव में एक निर्देश की आवश्यकता नहीं है (उदाहरण के लिए आपके सॉफ़्टवेयर में कहीं भी isinstance(x, dict) कोई कोड नहीं है, जैसा कि यह होना चाहिए), तो संभवतः आप UserDict.UserDict या UserDict.DictMixin का उपयोग करने से बेहतर हो सकते हैं यदि आप पाइथन < = 2.5, या collections.MutableMapping पर हैं यदि आप अजगर> = 2.6 पर हैं। वे आपकी कक्षा को एक उत्कृष्ट श्रद्धा व्यवहार के साथ प्रदान करेंगे।

संपादित करें: मैंने एक और टिप्पणी में पढ़ा है कि आप किसी भी विधि की विधि को ओवरराइड नहीं कर रहे हैं! फिर सबक्लासिंग में कोई बात नहीं है, ऐसा मत करो।

def createImageDb(directory): 
    d = {} 
    # do something to fill in the dict 
    return d 

संपादित करें 2: आप नए तरीकों को जोड़ने के लिए dict से उत्तराधिकारी होना चाहते हैं, लेकिन आपको किसी को ओवरराइड करने की आवश्यकता नहीं है। एक अच्छी पसंद से हो सकता है:

class MyContainer(dict): 
    def newmethod1(self, args): 
     pass 

    def newmethod2(self, args2): 
     pass 


def createImageDb(directory): 
    d = MyContainer() 
    # fill the container 
    return d 

वैसे: आप किन तरीकों को जोड़ रहे हैं? क्या आप वाकई एक अच्छा अमूर्त बना रहे हैं? हो सकता है कि आप बेहतर ढंग से एक वर्ग का उपयोग करें जो आपको आवश्यक विधियों को परिभाषित करता है और आंतरिक रूप से "सामान्य" नियम का उपयोग करता है।

फैक्टरी समारोह: http://en.wikipedia.org/wiki/Factory_method_pattern

यह बस एक समारोह के लिए एक उदाहरण के निर्माण सौंपने के बजाय अधिभावी/इसके निर्माताओं को बदलने का एक तरीका है।

+2

+1: सबक्लासिंग की आवश्यकता नहीं होने पर उप-वर्गीकरण करना एक बुरा विचार है, एक कारखाना बहुत बेहतर है। –

+0

भले ही मैं dict विधियों को ओवरराइड नहीं करता हूं, फिर भी नई कक्षा में अतिरिक्त विधियां हैं, ... (मैं कारखानों की जांच कर रहा हूं, सूचक के लिए धन्यवाद!) – EOL

+0

मुझे उपयोगकर्ता डिक्ट्यूब के बारे में निश्चित नहीं है: दस्तावेज पढ़ता है "यह मॉड्यूल भी एक वर्ग, उपयोगकर्ता डिक्ट्री को परिभाषित करता है, जो डिक्शनरी ऑब्जेक्ट्स के चारों ओर एक रैपर के रूप में कार्य करता है। इस वर्ग की आवश्यकता को मोटे तौर पर निर्देशित किया गया है (एक फीचर जो पाइथन संस्करण 2.2 से शुरू हो रही है)। – EOL

2

PEP 372 संग्रह मॉड्यूल को आदेशित आदेश जोड़ने के साथ संबंधित है।

यह चेतावनी देता है कि "उप-वर्गीकरण dict एक गैर-तुच्छ कार्य है और कई कार्यान्वयन सभी विधियों को ठीक से ओवरराइड नहीं करते हैं जो अप्रत्याशित परिणामों का कारण बन सकते हैं।"

प्रस्तावित (और स्वीकार किए जाते हैं) patch python3.1 करने के लिए उपयोग करता है एक __init__ कि इस तरह दिखता है:

+class OrderedDict(dict, MutableMapping): 
+ def __init__(self, *args, **kwds): 
+  if len(args) > 1: 
+   raise TypeError('expected at most 1 arguments, got %d' % len(args)) 
+  if not hasattr(self, '_keys'): 
+   self._keys = [] 
+  self.update(*args, **kwds) 

इस आधार पर यह dict.__init__() तरह लग रहा है कहा जा की जरूरत नहीं है

संपादित करें: यदि आप dict के किसी भी तरीके को ओवरराइड या विस्तारित नहीं कर रहे हैं, तो मैं एलन फ्रांज़ोनी से सहमत हूं: उप-वर्गीकरण के बजाए एक dict कारखाने का उपयोग करें:

def makeImageDB(*args,**kwargs): 
    d = {} 
    # modify d 
    return d 
+0

यह दिलचस्प है। अब, पायथन 3.1 के साथ 'dict .__ init __()' को कॉल नहीं करना सुरक्षित है, लेकिन भविष्य के बारे में क्या है? चूंकि मैं किसी भी विधि को ओवरराइड नहीं करता हूं, ImageDB में, सबक्लासिंग बहुत सुरक्षित है; केवल प्रारंभिक विशेष है (यह dict बनाता है)। – EOL

+0

क्षमा करें ईओएल, मैं आपका अनुसरण नहीं कर रहा हूं। मेरे दिमाग में, पायथन 3.1 भविष्य है ... :) – unutbu

+0

ध्यान में रखें कि वास्तव में init क्या कर रहा है। यह सभी तर्कों और खोजशब्दों के साथ निर्देश को अद्यतन करता है। यही वह वर्ग है जो आपकी कक्षा को करना होगा, इसलिए निर्देश देना। \ _ \ _ Init __ (self, * args, ** kwds) शायद आपके लिए इसका ख्याल रखता है, या आपको ऑर्डरर्ड डिक्ट की तरह self.update को कॉल करना होगा कर देता है। –

10

आपको आम तौर पर बेस क्लास '__init__ पर कॉल करना चाहिए, तो यहां अपवाद क्यों करें?

या तो __init__ पर हावी नहीं है या आप __init__ कॉल आधार वर्ग __init__ ओवरराइड करने के लिए की जरूरत है, आप तर्क के बारे में चिंता है, तो बस * args, ** kwargs या कुछ भी नहीं अगर आप चाहते हैं खाली dict उदा पारित

class MyDict(dict): 
    def __init__(self, *args, **kwargs): 
     myparam = kwargs.pop('myparam', '') 
     dict.__init__(self, *args, **kwargs) 

हम क्या baseclass कर रहा है या नहीं कर रही नहीं मानना ​​चाहिए, यह गलत है नहीं आधार वर्ग कॉल करने के लिए __init__

+0

को '__init__'' को कॉल करना वास्तव में क्या है मैं वर्तमान में कर रहा हूँ चूंकि ऐसा लगता है कि बिना किसी तर्क के इसे कॉल करना कुछ भी नहीं करता है, इसलिए मैं पाइथन के बारे में मौलिक तथ्यों के बारे में उत्सुक हूं जो इसे कॉल नहीं करने की अनुमति देगा! – EOL

+0

@EOL, IMO यह केवल सादा गलत है कि बेसक्लास __init__ को कॉल न करें, अन्यथा –

+0

@Anurag करने के लिए बहुत ही मजबूत कारण है: मैं आपका बिंदु देखता हूं। मैं पाइथन के बारे में अपने ज्ञान को थोड़ी और आगे बढ़ाने की कोशिश कर रहा हूं, और सोच रहा था कि 'dict.__ init __ (self) '(किसी अन्य तर्क के साथ) नहीं कहने के लिए ऐसा" बहुत ही मजबूत कारण "मौजूद है (जैसे" यह होगा कभी भी कुछ नहीं करें ")। – EOL

3

जब dict उपवर्गीकरण अचार बनाने से सावधान रहें; उदाहरण के लिए 2.7, में __getnewargs__ की आवश्यकता है और पुराने संस्करणों में शायद __getstate__ __setstate__ की आवश्यकता है। (मुझे कोई जानकारी नहीं है की क्यों।)

class Dotdict(dict): 
    """ d.key == d["key"] """ 

    def __init__(self, *args, **kwargs): 
     dict.__init__(self, *args, **kwargs) 
     self.__dict__ = self 

    def __getnewargs__(self): # for cPickle.dump(d, file, protocol=-1) 
     return tuple(self) 
संबंधित मुद्दे