सुपरक्लास __init__ विधियां स्वचालित रूप से क्यों लागू नहीं होती हैं?


155

पायथन डिजाइनरों ने यह क्यों तय किया कि उपवर्गों के __init__()तरीके __init__()अपने सुपरक्लेसेस के तरीकों को स्वचालित रूप से नहीं कहते हैं , जैसा कि कुछ अन्य भाषाओं में है? क्या पायथोनिक और अनुशंसित मुहावरे वास्तव में निम्नलिखित की तरह हैं?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'

2
आप __init__विधि को इनहेरिट करने के लिए डेकोरेटर लिख सकते हैं , और यहां तक ​​कि शायद स्वचालित रूप से उपवर्गों को खोज सकते हैं और उन्हें सजा सकते हैं।
oss

2
@ एक बहुत अच्छा विचार लगता है। क्या आप डेकोरेटर भाग पर थोड़ा और वर्णन करना चाहेंगे?
डायनेशेंग

1
@ ओसा हां और वर्णन करें!
चार्ली पार्कर

जवाबों:


163

पायथन के बीच महत्वपूर्ण अंतर __init__है और उन अन्य भाषाओं कंस्ट्रक्टर्स वह यह है कि __init__है नहीं यह एक है: एक निर्माता प्रारंभकर्ता (वास्तविक निर्माता , (यदि कोई हो, लेकिन देखना बाद में ;-) है __new__और काम करता है पूरी तरह से बिल्कुल अलग तरीके से)। सभी सुपरक्लासेस का निर्माण करते समय (और, इसमें कोई संदेह नहीं है, "ऐसा करने से पहले" आप नीचे की ओर निर्माण जारी रखते हैं) स्पष्ट रूप से यह कहने का हिस्सा है कि आप एक उप-वर्ग के उदाहरण का निर्माण कर रहे हैं , यह स्पष्ट रूप से प्रारंभ करने के मामले में नहीं है, क्योंकि ऐसे कई उपयोग के मामले हैं जिनमें सुपरक्लास की इनिशियलाइज़ेशन को छोड़ना पड़ता है, बदल दिया जाता है, नियंत्रित किया जाता है - हो रहा है, यदि बिल्कुल भी, तो सबक्लास इनिशियलाइज़ेशन के "बीच में", और आगे।

मूल रूप से, इनिशियलाइज़र का सुपर-क्लास डेलिगेशन ठीक उसी कारणों से पाइथन में स्वचालित नहीं है जैसे कि यह प्रतिनिधिमंडल किसी भी अन्य तरीकों के लिए भी स्वचालित नहीं है - और ध्यान दें कि वे "अन्य भाषाएं" किसी भी के लिए स्वचालित सुपर-क्लास प्रतिनिधिमंडल नहीं करते हैं अन्य विधि या तो ... बस निर्माता के लिए (और यदि लागू हो, नाशक) है, जो, जैसा कि मैंने उल्लेख किया है, है नहीं क्या पायथन के __init__है। (का व्यवहार __new__भी काफी अजीब है, हालांकि वास्तव में सीधे आपके प्रश्न से संबंधित नहीं है, क्योंकि __new__एक ऐसा अजीब रचनाकार है जिसे वास्तव में कुछ भी बनाने की आवश्यकता नहीं है - पूरी तरह से मौजूदा उदाहरण, या यहां तक ​​कि एक गैर-उदाहरण भी लौटा सकता है। ... स्पष्ट रूप से अजगर आपको बहुत कुछ प्रदान करता है"अन्य भाषाओं" आपके मन में है, जो की तुलना में यांत्रिकी के अधिक नियंत्रण भी में कोई स्वत: प्रतिनिधिमंडल होने शामिल __new__!) - अपने आप में।


7
मेरे लिए अपवित्र क्योंकि वास्तविक लाभ वह क्षमता है जिसका उल्लेख आप उपवर्ग के आरंभ में किसी भी बिंदु पर सुपरक्लास के इनिट () _ को कॉल करने के लिए करते हैं (या बिल्कुल नहीं)।
किंडल

53
-1 के लिए " __init__एक कंस्ट्रक्टर नहीं है ... वास्तविक कंस्ट्रक्टर ... __new__" है। जैसा कि आप स्वयं ध्यान दें, __new__अन्य भाषाओं से एक निर्माता की तरह कुछ भी व्यवहार नहीं करता है। __init__वास्तव में बहुत समान है (यह एक नई वस्तु के निर्माण के दौरान कहा जाता है, उस वस्तु को आवंटित करने के बाद , नई वस्तु पर सदस्य चर सेट करने के लिए), और लगभग हमेशा कार्यक्षमता को लागू करने का स्थान है जिसे आप अन्य भाषाओं में डालेंगे। एक निर्माता। तो बस इसे कंस्ट्रक्टर कहें!
बेन

8
मुझे भी लगता है कि यह एक बेतुका बयान है: " सभी सुपरक्लासेस का निर्माण ... स्पष्ट रूप से यह कहने का हिस्सा है कि आप एक उप-वर्ग के उदाहरण का निर्माण कर रहे हैं , यह स्पष्ट रूप से प्रारंभिक के लिए मामला नहीं है "। शब्द निर्माण / आरंभीकरण में ऐसा कुछ नहीं है जो किसी को भी यह "स्पष्ट" बनाता हो। और __new__स्वचालित रूप से __new__या तो सुपरक्लास आह्वान नहीं करता है । तो आपका दावा है कि मुख्य अंतर यह है कि निर्माण में अनिवार्य रूप से सुपरक्लास का निर्माण शामिल है, जबकि आरंभीकरण आपके दावे के साथ असंगत नहीं है कि __new__निर्माणकर्ता है।
बेन

36
वास्तव में, docs.python.org/reference/datamodel.html#basic-customization__init__ पर python डॉक्स से : " कंस्ट्रक्टरों पर एक विशेष बाधा के रूप में , कोई मूल्य नहीं लौटाया जा सकता है; ऐसा करने से रनटाइम पर एक TypeError पैदा होगी। ”(जोर देकर)। तो वहाँ, यह आधिकारिक है, एक निर्माता है। __init__
बेन

3
पायथन / जावा शब्दावली में, __init__एक कंस्ट्रक्टर कहा जाता है। यह कंस्ट्रक्टर एक इनिशियलाइज़ेशन फंक्शन है जिसे ऑब्जेक्ट पूरी तरह से निर्मित होने के बाद और एक डिफ़ॉल्ट स्थिति में इनिशियलाइज़ किया जाता है, जिसमें उसका फाइनल रनटाइम टाइप भी शामिल है। यह C ++ कंस्ट्रक्टर्स के समतुल्य नहीं है, जिसे एक अपरिभाषित राज्य के साथ एक स्टेटिकली टाइप, एलोकेटेड ऑब्जेक्ट पर कॉल किया जाता है। ये भी भिन्न हैं __new__, इसलिए हमारे पास कम से कम चार अलग-अलग प्रकार के आवंटन / निर्माण / आरंभीकरण कार्य हैं। भाषा मिश्रित शब्दावली का उपयोग करती है, और महत्वपूर्ण हिस्सा व्यवहार है न कि शब्दावली।
एलाजार

36

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

कारण सरल है: आप जरूरी नहीं कि आप एक तरह से व्युत्पन्न वर्ग का निर्माण करें कि आप आधार वर्ग का निर्माण कैसे करें। आपके पास अधिक पैरामीटर हो सकते हैं, कम, वे एक अलग क्रम में हो सकते हैं या बिल्कुल भी संबंधित नहीं हो सकते हैं।

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")

यह सभी व्युत्पन्न तरीकों पर लागू होता है, न कि सिर्फ __init__


6
क्या यह उदाहरण कुछ खास पेश करता है? स्थिर भाषाएं यह भी कर सकती हैं
jean

18

जावा और सी ++ की आवश्यकता है कि एक आधार वर्ग निर्माता स्मृति लेआउट की वजह से कहा जाता है।

यदि आपके पास एक BaseClassसदस्य के साथ एक वर्ग है field1, और आप एक नया वर्ग बनाते SubClassहैं जो एक सदस्य जोड़ता है field2, तो एक उदाहरण SubClassजिसमें field1और के लिए स्थान होता है field2। आपको BaseClassभरने के लिए एक कंस्ट्रक्टर की field1आवश्यकता होती है, जब तक कि आपको BaseClassअपने स्वयं के कंस्ट्रक्टरों में पुनरावृत्ति की आरम्भिक पुनरावृत्ति की आवश्यकता न हो । और अगर field1निजी है, तो इनहेरिट करने वाली कक्षाएं आरंभ नहीं कर सकती हैंfield1

अजगर जावा या C ++ नहीं है। सभी उपयोगकर्ता-परिभाषित वर्गों के सभी उदाहरणों में समान 'आकार' होता है। वे मूल रूप से केवल शब्दकोष हैं जिनमें विशेषताएँ डाली जा सकती हैं। इससे पहले कि कोई आरंभीकरण किया गया है, सभी उपयोगकर्ता-परिभाषित वर्गों के सभी उदाहरण लगभग समान हैं ; वे केवल उन विशेषताओं को संग्रहीत करने के लिए स्थान हैं जो अभी तक संग्रहीत नहीं हैं।

तो यह पायथन सबक्लास के लिए इसका सही अर्थ है कि वह अपने बेस क्लास कंस्ट्रक्टर को न बुलाए। यह केवल विशेषताओं को जोड़ सकता है यदि वह चाहता था। पदानुक्रम में प्रत्येक वर्ग के लिए दिए गए फ़ील्ड के लिए कोई स्थान आरक्षित नहीं है, और किसी BaseClassविधि से कोड द्वारा जोड़े गए विशेषता और किसी विधि से कोड द्वारा जोड़े गए विशेषता के बीच कोई अंतर नहीं है SubClass

अगर, जैसा कि आम है, SubClassवास्तव में सभी BaseClassअपने स्वयं के अनुकूलन करने के लिए जाने से पहले स्थापित होने वाले सभी आक्रमणकारियों को चाहते हैं, तो हाँ आप बस कॉल कर सकते हैं BaseClass.__init__()(या उपयोग कर सकते हैं super, लेकिन यह जटिल है और कभी-कभी इसकी अपनी समस्याएं हैं)। लेकिन आपके पास नहीं है। और आप इसे पहले, या बाद में, या विभिन्न तर्कों के साथ कर सकते हैं। नरक, अगर आप चाहते थे कि आप BaseClass.__init__पूरी तरह से एक और विधि से कॉल कर सकते हैं __init__; हो सकता है कि आपके पास कुछ विचित्र आलसी आरंभीकरण की बात हो।

पायथन चीजों को सरल रखकर इस लचीलेपन को प्राप्त करता है। आप एक ऐसी __init__विधि लिखकर ऑब्जेक्ट को इनिशियलाइज़ करते हैं, जो एट्रीब्यूट सेट करती है self। बस। यह बिल्कुल एक विधि की तरह व्यवहार करता है, क्योंकि यह बिल्कुल एक विधि है। पहले किए जाने वाली चीजों के बारे में कोई अन्य अजीब और अनूठे नियम नहीं हैं, या यदि आप अन्य चीजों को नहीं करते हैं तो चीजें अपने आप हो जाएंगी। एकमात्र उद्देश्य जिसे सेवा देना है, वह है प्रारंभिक गुण मानों को सेट करने के लिए ऑब्जेक्ट इनिशियलाइज़ेशन के दौरान निष्पादित करने के लिए एक हुक होना, और यह बस यही करता है। यदि आप चाहते हैं कि यह कुछ और करे, तो आप स्पष्ट रूप से अपने कोड में लिखें।


2
C ++ के लिए, इसका "मेमोरी लेआउट" से कोई लेना-देना नहीं है। पायथन ऑफर के समान इनिशियलाइज़ेशन मॉडल C ++ में लागू किया जा सकता था। एकमात्र कारण है कि निर्माण / विनाश जिस तरह से वे सी ++ में हैं, वह संसाधन प्रबंधन (आरएआईआई) के लिए विश्वसनीय और अच्छी तरह से व्यवहार करने की सुविधा प्रदान करने के लिए डिजाइन निर्णय के कारण है, जो स्वचालित रूप से उत्पन्न हो सकता है (जिसका अर्थ कम कोड और मानवीय त्रुटियां हैं) कंपाइलरों द्वारा उनके (उनके कॉल ऑर्डर) के नियमों को कड़ाई से परिभाषित किया गया है। निश्चित रूप से नहीं, लेकिन जावा ने संभवतः इस दृष्टिकोण का अनुसरण करते हुए अभी तक एक और पोला सी जैसी भाषा का उपयोग किया है।
अलेक्जेंडर शुकेव

10

"स्पष्ट रूप से निहितार्थ से बेहतर है।" यह वही तर्क है जो इंगित करता है कि हमें स्पष्ट रूप से 'स्व' लिखना चाहिए।

मुझे लगता है कि अंत में यह एक लाभ है - क्या आप उन सभी नियमों का पाठ कर सकते हैं जिनके बारे में जावा ने सुपरक्लास के कंस्ट्रक्टर्स को कॉल करने के बारे में है?


8
मैं आपके अधिकांश भाग के लिए सहमत हूं, लेकिन जावा का नियम वास्तव में बहुत सरल है: नो-आर्ग कंस्ट्रक्टर को तब तक लागू किया जाता है जब तक कि आप विशेष रूप से एक अलग के लिए नहीं पूछते।
लॉरेंस गोंसाल्वेस

1
@ लॉरेंस - क्या होता है जब मूल वर्ग एक नो-आर्ग कंस्ट्रक्टर को परिभाषित नहीं करता है? क्या होता है जब नो-आर्ग कंस्ट्रक्टर संरक्षित या निजी होता है?
माइक एसेक

8
यदि आप इसे स्पष्ट रूप से कहने का प्रयास करते हैं तो वास्तव में वही बात होगी।
लॉरेंस गोंसाल्वेस

8

अक्सर उपवर्ग में अतिरिक्त पैरामीटर होते हैं जिन्हें सुपरक्लास को पारित नहीं किया जा सकता है।


7

अभी, हमारे पास कई विरासत के मामले में विधि संकल्प क्रम का वर्णन करने वाला एक लंबा पृष्ठ है: http://www.python.org/download/releases/2.3/mro/

यदि कंस्ट्रक्टर को स्वचालित रूप से कॉल किया गया था, तो आपको उस घटना के आदेश को समझाते हुए कम से कम एक ही पृष्ठ की आवश्यकता होगी। यह नरक होगा ...


यह सही जवाब था। पायथन को उपवर्ग और सुपरक्लास के बीच पारित होने वाले तर्क के शब्दार्थ को परिभाषित करने की आवश्यकता होगी। बहुत बुरा यह जवाब नहीं है। शायद अगर यह कुछ उदाहरणों के साथ समस्या दिखाता है?
गैरी वीज़

5

भ्रम से बचने के लिए यह जानना उपयोगी है कि आप आधार_क्लास __init__()विधि को लागू कर सकते हैं यदि बच्चे के पास __init__()क्लास नहीं है ।

उदाहरण:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1

वास्तव में अजगर में एमआरओ __init__()को अभिभावक वर्ग की तलाश होगी जब वह बच्चों की कक्षा में नहीं मिल सकता है। यदि आप पहले से ही __init__()बच्चे वर्ग में एक विधि है, तो आपको सीधे पैरेंट क्लास कंस्ट्रक्टर को आमंत्रित करना होगा ।

उदाहरण के लिए, निम्न कोड एक त्रुटि लौटाएगा: कक्षा अभिभावक: init (आत्म, a = 1, b = 0): self.a = a self.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.

3

शायद __init__वह विधि है जिसे उपवर्ग को ओवरराइड करने की आवश्यकता होती है। कभी-कभी उपवर्गों को कक्षा-विशिष्ट कोड जोड़ने से पहले चलाने के लिए माता-पिता के कार्य की आवश्यकता होती है, और अन्य बार उन्हें माता-पिता के फ़ंक्शन को कॉल करने से पहले उदाहरण चर सेट करने की आवश्यकता होती है। चूंकि पायथन को कोई रास्ता नहीं पता है, इसलिए संभवतः उन कार्यों को कॉल करने के लिए सबसे उपयुक्त होगा, यह अनुमान नहीं लगाना चाहिए।

यदि वे आपको नहीं बहाते हैं, तो विचार करें कि __init__बस एक और कार्य है। यदि विचाराधीन फ़ंक्शन dostuffइसके बजाय था, तो क्या आप अभी भी पायथन को मूल वर्ग में संबंधित फ़ंक्शन को स्वचालित रूप से कॉल करना चाहेंगे?


2

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

सब के बाद, सिर्फ इसलिए कि कक्षा बी कक्षा ए से ली गई है, इसका मतलब यह नहीं है कि A.__init__()उसे उसी तर्क के साथ बुलाया जा सकता है या नहीं B.__init__()। कॉल को स्पष्ट करने का मतलब है कि एक प्रोग्रामर उदाहरण B.__init__()के लिए पूरी तरह से अलग मापदंडों के साथ परिभाषित कर सकता है , उस डेटा के साथ कुछ गणना करें, A.__init__()उस पद्धति के लिए उपयुक्त के रूप में तर्कों के साथ कॉल करें , और फिर कुछ पोस्टप्रोसेसिंग करें। इस तरह के लचीलेपन को प्राप्त करने के लिए अजीब A.__init__()होगा यदि इसे B.__init__()अंतर्निहित रूप से बुलाया जाएगा , या तो B.__init__()निष्पादित करने से पहले या इसके ठीक बाद।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.