python - __Init__() विधियों के साथ पाइथन सुपर() को समझना




class oop (5)

इस प्रश्न का उत्तर यहां दिया गया है:

मैं super() के उपयोग को समझने की कोशिश कर रहा हूं। इसकी प्रकृति से, दोनों बच्चे वर्गों को बनाया जा सकता है, बस ठीक है।

मैं निम्नलिखित 2 बाल वर्गों के बीच वास्तविक अंतर के बारे में जानना चाहता हूं।

class Base(object):
    def __init__(self):
        print "Base created"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()

ChildA() 
ChildB()

मैं super() समझने की कोशिश कर रहा हूँ super()

हम super का उपयोग करने का कारण यह है कि सहकारी एकाधिक विरासत का उपयोग करने वाले बच्चे वर्ग विधि समाधान आदेश (एमआरओ) में सही अगले मूल वर्ग समारोह को कॉल करेंगे।

पायथन 3 में, हम इसे इस तरह से कॉल कर सकते हैं:

class ChildB(Base):
    def __init__(self):
        super().__init__() 

पायथन 2 में, हमें इसका उपयोग करने की आवश्यकता है:

        super(ChildB, self).__init__()

सुपर के बिना, आप एकाधिक विरासत का उपयोग करने की अपनी क्षमता में सीमित हैं:

        Base.__init__(self) # Avoid this.

मैं आगे नीचे बताता हूं।

"वास्तव में इस कोड में क्या अंतर है ?:"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()
        # super().__init__() # you can call super like this in Python 3!

इस कोड में प्राथमिक अंतर यह है कि आपको super साथ __init__ में __init__ की एक परत मिलती है, जो एमआरओ में देखने के लिए अगली कक्षा के __init__ को निर्धारित करने के लिए वर्तमान कक्षा का उपयोग करती है।

मैं इस अंतर को कैननिकल प्रश्न के उत्तर में पाइथन में 'सुपर' का उपयोग कैसे करूं? , जो निर्भरता इंजेक्शन और सहकारी एकाधिक विरासत का प्रदर्शन करता है

अगर पाइथन में super नहीं था

यहां कोड है जो वास्तव में super बराबर है (यह सी में कैसे कार्यान्वित किया गया है, कुछ जांच और फ़ॉलबैक व्यवहार से कम है, और पायथन में अनुवाद किया गया है):

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()             # Get the Method Resolution Order.
        check_next = mro.index(ChildB) + 1 # Start looking after *this* class.
        while check_next < len(mro):
            next_class = mro[check_next]
            if '__init__' in next_class.__dict__:
                next_class.__init__(self)
                break
            check_next += 1

देशी पायथन की तरह थोड़ा और लिखा:

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
            if hasattr(next_class, '__init__'):
                next_class.__init__(self)
                break

अगर हमारे पास super ऑब्जेक्ट नहीं था, तो हमें यह मैन्युअल कोड लिखना होगा (या इसे फिर से बनाना!) यह सुनिश्चित करने के लिए कि हम विधि समाधान आदेश में उचित अगली विधि को कॉल करें!

पाइथन 3 में सुपर यह कैसे करता है बिना स्पष्ट रूप से बताया जा रहा है कि किस वर्ग और उदाहरण से इसे कहा गया था?

यह कॉलिंग स्टैक फ्रेम प्राप्त करता है, और कक्षा को (स्थानीय रूप से स्थानीय फ्री वैरिएबल, __class__ रूप में संग्रहीत किया जाता है, जो कॉलिंग फ़ंक्शन को क्लास पर बंद कर देता है) और उस फ़ंक्शन के लिए पहला तर्क, जो उदाहरण या कक्षा होना चाहिए जो इसे सूचित करता है उपयोग करने के लिए विधि समाधान आदेश (एमआरओ)।

चूंकि इसे आवश्यक है कि एमआरओ के लिए पहला तर्क, स्थिर विधियों के साथ super का उपयोग करना असंभव है

अन्य उत्तरों की आलोचनाएं:

सुपर () आपको बेस क्लास को स्पष्ट रूप से संदर्भित करने से बचने देता है, जो अच्छा हो सकता है। । लेकिन मुख्य लाभ कई विरासत के साथ आता है, जहां मजेदार सामान के सभी प्रकार हो सकते हैं। यदि आप पहले से नहीं हैं तो सुपर पर मानक दस्तावेज़ देखें।

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

मैं यहां समझाऊंगा।

class Base(object):
    def __init__(self):
        print("Base init'ed")

class ChildA(Base):
    def __init__(self):
        print("ChildA init'ed")
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        print("ChildB init'ed")
        super(ChildB, self).__init__()

और आइए निर्भरता बनाएं कि हम बच्चे के बाद बुलाए जाएं:

class UserDependency(Base):
    def __init__(self):
        print("UserDependency init'ed")
        super(UserDependency, self).__init__()

अब याद रखें, ChildB सुपर का उपयोग करता है, ChildA नहीं करता है:

class UserA(ChildA, UserDependency):
    def __init__(self):
        print("UserA init'ed")
        super(UserA, self).__init__()

class UserB(ChildB, UserDependency):
    def __init__(self):
        print("UserB init'ed")
        super(UserB, self).__init__()

और UserA उपयोगकर्ता निर्भरता विधि को कॉल नहीं करता है:

>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>

लेकिन उपयोगकर्ताबी, क्योंकि ChildB super का उपयोग करता है, करता है !:

>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>

एक और जवाब के लिए आलोचना

किसी भी परिस्थिति में आपको निम्न कार्य नहीं करना चाहिए, जो एक और उत्तर सुझाता है, क्योंकि जब आप चाइल्डबी को उप-वर्गीकृत करते हैं तो आपको निश्चित रूप से त्रुटियां मिलेंगी:

        super(self.__class__, self).__init__() # Don't do this. Ever.

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

स्पष्टीकरण: उस उत्तर ने सुपर को कॉल करने का सुझाव दिया:

super(self.__class__, self).__init__()

यह पूरी तरह गलत है। super हमें बाल कक्षाओं के लिए एमआरओ में अगले माता-पिता को देखने दें (इस उत्तर का पहला भाग देखें)। यदि आप super बताते हैं कि हम बच्चे के उदाहरण की विधि में हैं, तो यह अगली विधि को लाइन में देखेगा (शायद यह एक) जिसके परिणामस्वरूप रिकर्सन हो सकता है, संभवतः एक तार्किक विफलता (उत्तर देने वाले के उदाहरण में, यह करता है) या एक RuntimeError रिकर्सन गहराई पार हो गई है।

>>> class Polygon(object):
...     def __init__(self, id):
...         self.id = id
...
>>> class Rectangle(Polygon):
...     def __init__(self, id, width, height):
...         super(self.__class__, self).__init__(id)
...         self.shape = (width, height)
...
>>> class Square(Rectangle):
...     pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'

मुख्य अंतर यह है कि ChildA.__init__ बिना किसी शर्त के Base.__init__ कॉल करेगा Base.__init__ जबकि ChildB.__init__ __init__ को कॉल करेगा, जो भी कक्षा में पूर्वजों की self की रेखा में ChildB पूर्वज होता है (जो आप अपेक्षा करते हैं उससे भिन्न हो सकते हैं)।

यदि आप एक ClassC जो एकाधिक विरासत का उपयोग करता है:

class Mixin(Base):
  def __init__(self):
    print "Mixin stuff"
    super(Mixin, self).__init__()

class ChildC(ChildB, Mixin):  # Mixin is now between ChildB and Base
  pass

ChildC()
help(ChildC) # shows that the the Method Resolution Order is ChildC->ChildB->Mixin->Base

तो Base अब ChildC उदाहरणों के लिए ChildC का अभिभावक नहीं है । अब super(ChildB, self) को इंगित करेगा यदि self एक ChildC उदाहरण है।

आपने ChildB और Base बीच ChildB डाला है। और आप super() साथ इसका लाभ उठा सकते हैं

इसलिए यदि आप अपनी कक्षाएं डिज़ाइन कर चुके हैं ताकि उन्हें सहकारी एकाधिक विरासत परिदृश्य में उपयोग किया जा सके, तो आप super उपयोग करते हैं क्योंकि आप वास्तव में नहीं जानते कि रनटाइम पर पूर्वजों के लिए कौन जा रहा है।

वीडियो के साथ सुपर माना गया सुपर पोस्ट और पायकॉन 2015 इस सुंदर अच्छी तरह से समझाता है।


वास्तव में नहीं है। super() विधियों को कॉल करने के लिए एमआरओ (विधि संकल्प आदेश, cls.__mro__ साथ cls.__mro__ ) में अगली कक्षा को cls.__mro__ । बस __init__ आधार को कॉल करने से आधार __init__ कॉल किया जाता है। जैसा कि होता है, एमआरओ में बिल्कुल एक वस्तु है - आधार। तो आप वास्तव में एक ही चीज़ कर रहे हैं, लेकिन super() साथ एक अच्छे तरीके से (विशेष रूप से यदि आप बाद में एकाधिक विरासत में आते हैं)।


सिर्फ एक सिर ऊपर ... पायथन 2.7 के साथ, और मेरा मानना ​​है कि super() संस्करण 2.2 में पेश किया गया था, तो आप केवल super() कॉल कर सकते हैं यदि माता-पिता में से एक वर्ग उस वर्ग से प्राप्त होता है जो अंततः object ( नई शैली की कक्षाएं )।

व्यक्तिगत रूप से, पायथन 2.7 कोड के लिए, मैं बेस BaseClassName.__init__(self, args) का उपयोग जारी रखने जा रहा हूं BaseClassName.__init__(self, args) जब तक कि मुझे वास्तव में super() का उपयोग करने का लाभ नहीं मिलता है।


super() आपको बेस क्लास को स्पष्ट रूप से संदर्भित करने से बचने देता है, जो अच्छा हो सकता है। लेकिन मुख्य लाभ कई विरासत के साथ आता है, जहां मजेदार सामान के सभी प्रकार हो सकते हैं। यदि आप पहले से नहीं हैं तो सुपर पर मानक दस्तावेज़ देखें।

ध्यान दें कि पायथन 3.0 में सिंटैक्स बदल गया है : आप super(ChildB, self).__init__() बजाय super().__init__() कह सकते हैं super(ChildB, self).__init__() जो आईएमओ काफी अच्छा है। मानक दस्तावेज़ भी सुपर () का उपयोग करने के लिए एक गाइड का संदर्भ देते हैं जो काफी व्याख्यात्मक है।





super