पायथन का सुपर () मल्टीपल इनहेरिटेंस के साथ कैसे काम करता है?


885

मैं पायथन ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग में बहुत नया हूं और मुझे super()फंक्शन (नई शैली की कक्षाएं) को समझने में परेशानी होती है, खासकर जब यह मल्टीपल इनहेरिटेंस की बात आती है।

उदाहरण के लिए यदि आपके पास कुछ है:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

मुझे जो नहीं मिलता है वह यह है: क्या Third()क्लास दोनों कंस्ट्रक्टर के तरीकों को विरासत में देगी ? यदि हाँ, तो कौन सुपर () के साथ चलाया जाएगा और क्यों?

और क्या होगा अगर आप दूसरे को चलाना चाहते हैं? मुझे पता है कि इसे पायथन विधि संकल्प क्रम ( एमआरओ ) के साथ कुछ करना है ।


वास्तव में, एकाधिक उत्तराधिकार ही एकमात्र ऐसा मामला है जहां super()किसी काम का नहीं है। मैं इसे रैखिक वंशानुक्रम का उपयोग करके कक्षाओं के साथ उपयोग करने की अनुशंसा नहीं करूंगा, जहां यह सिर्फ बेकार उपरि है।
बछसौ

9
@ बाक्सौ तकनीकी रूप से सही है कि यह एक छोटा ओवरहेड है, लेकिन सुपर () अधिक पायथोनिक है और समय के साथ कोड को फिर से फैक्टरिंग और बदलाव के लिए अनुमति देता है। सुपर () का उपयोग करें जब तक आपको वास्तव में नामित वर्ग विशिष्ट विधि की आवश्यकता न हो।
पॉल व्हिप

2
इसके साथ एक और समस्या super()यह है कि यह हर उपवर्ग को इसका उपयोग करने के लिए मजबूर करता है, जबकि उपयोग न करने पर super(), हर कोई उपवर्ग इसे स्वयं तय कर सकता है। यदि इसका उपयोग करने वाला कोई डेवलपर इसके बारे में नहीं जानता है super()या नहीं जानता है कि इसका उपयोग किया गया था, तो एमआरओ के साथ समस्याएं पैदा हो सकती हैं जो नीचे ट्रैक करने के लिए बहुत कठिन हैं।
बाक्साऊ

जवाबों:


710

यह अपने ब्लॉग पोस्ट मेथड रेजोल्यूशन ऑर्डर (दो पहले के प्रयासों सहित) में स्वयं गिदो द्वारा विस्तार से उचित मात्रा में दिया गया है ।

आपके उदाहरण में, Third()कॉल करेगा First.__init__। पायथन वर्ग के माता-पिता में प्रत्येक विशेषता के लिए दिखता है क्योंकि वे बाएं से दाएं सूचीबद्ध हैं। इस मामले में, हम तलाश कर रहे हैं __init__। इसलिए, यदि आप परिभाषित करते हैं

class Third(First, Second):
    ...

अजगर को देखकर शुरू हो जाएगा First, और, अगर Firstविशेषता नहीं है, तो यह दिखेगा Second

यह स्थिति तब और जटिल हो जाती है जब वंशानुक्रम पथ को पार करना शुरू कर देता है (उदाहरण के लिए यदि Firstविरासत में से Second)। अधिक विवरण के लिए ऊपर दिए गए लिंक को पढ़ें, लेकिन, संक्षेप में, पायथन उस क्रम को बनाए रखने की कोशिश करेगा जिसमें प्रत्येक वर्ग वंशानुक्रम सूची में दिखाई देता है, जो कि बाल वर्ग के साथ ही शुरू होता है।

उदाहरण के लिए, यदि आपके पास था:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

एमआरओ होगा [Fourth, Second, Third, First].

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

अस्पष्ट एमआरओ का एक उदाहरण जोड़ने के लिए संपादित:

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

क्या Thirdएमआरओ होना चाहिए [First, Second]या [Second, First]? कोई स्पष्ट उम्मीद नहीं है, और पायथन एक त्रुटि उठाएंगे:

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

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

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)

12
यह तब और अधिक दिलचस्प हो जाता है (और, यकीनन, अधिक भ्रमित) जब आप पहले, दूसरे और तीसरे [ pastebin.com/ezTyZ5Wa ] में सुपर () कॉल करना शुरू करते हैं ।
गातोतिग्रादो

52
मुझे लगता है कि पहली कक्षाओं में सुपर कॉल की कमी इस जवाब के साथ एक बहुत बड़ी समस्या है; इस बात पर चर्चा किए बिना कि कैसे / क्यों महत्वपूर्ण महत्वपूर्ण समझ प्रश्न के लिए खो गई है।
सैम हार्टमैन

3
यह उत्तर केवल गलत है। माता-पिता में सुपर () कॉल के बिना, कुछ भी नहीं होगा। @ बेजान जवाब सही है।
सेरिन

8
@Cerin इस उदाहरण का बिंदु यह दिखाना है कि MRO का निर्माण कैसे किया जाता है। उदाहरण "पहले \ nsecond \ third" या जो भी प्रिंट करने का इरादा नहीं है। और एमआरओ वास्तव में सही है: चौथा .__ mro__ == (<वर्ग ' मुख्य .Fourth'>, <वर्ग ' मुख्य .Second'>, <वर्ग ' मुख्य .Third'>, <वर्ग ' मुख्य प्रथम'>, < टाइप 'ऑब्जेक्ट'>)
आरबीपी

2
जहाँ तक मैं देख सकता हूँ, यह उत्तर ओपी के प्रश्नों में से एक को याद कर रहा है, जो "और यदि आप दूसरे को चलाना चाहते हैं तो क्या है?"। मैं इस प्रश्न का उत्तर देखना चाहता हूं। क्या हम केवल स्पष्ट रूप से आधार वर्ग का नाम रखने वाले हैं?
रे

251

आपका कोड, और अन्य उत्तर, सभी छोटी गाड़ी हैं। वे super()पहले दो वर्गों में कॉल को याद कर रहे हैं जो काम करने के लिए सहकारी उपवर्ग के लिए आवश्यक हैं।

यहाँ कोड का एक निश्चित संस्करण है:

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print("first")

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print("second")

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print("third")

super()कॉल हर कदम है, जिसके कारण पहले और दूसरे यह भी करना होगा पर एमआरओ में अगली विधि पाता है, अन्यथा निष्पादन के अंत में बंद हो जाता है Second.__init__()

यह वही है जो मुझे मिलता है:

>>> Third()
second
first
third

89
यदि इन वर्गों को खुद को शुरू करने के लिए अलग-अलग मापदंडों की आवश्यकता हो तो क्या करें?
कैलफज़ो

2
"सहकारी उपवर्ग"
क्वांट मेट्रोपोलिस

5
इस तरह बीओटीएच बेस कक्षाओं के इनिट तरीकों को निष्पादित किया जाएगा, जबकि मूल उदाहरण एमआरओ में सामने आए पहले इनिट को ही कहते हैं । मुझे लगता है कि यह शब्द "सहकारी उपवर्ग" द्वारा निहित है, लेकिन एक स्पष्टीकरण उपयोगी होगा ('स्पष्ट रूप से निहित से बेहतर है', आप जानते हैं;))
क्वांट मेट्रोपोलिस

1
हां, यदि आप किसी पैरामीटर को सुपर के माध्यम से बुलाया जा रहा है, तो उस विधि के सभी कार्यान्वयन एमआरओ को ऑब्जेक्ट () की ओर ले जा रहे हैं, जिसके लिए संगत हस्ताक्षर होने चाहिए। यह कीवर्ड मापदंडों के माध्यम से प्राप्त किया जा सकता है: विधि के उपयोग की तुलना में अधिक मापदंडों को स्वीकार करें, और अतिरिक्त लोगों को अनदेखा करें। इसका आमतौर पर ऐसा करने के लिए बदसूरत माना जाता है, और ज्यादातर मामलों में नए तरीकों को जोड़ना बेहतर होता है, लेकिन init (लगभग) एक विशेष विधि के नाम के रूप में अद्वितीय है लेकिन उपयोगकर्ता परिभाषित मापदंडों के साथ है।
बेजान

15
कई विरासत का डिज़ाइन वास्तव में अजगर में बहुत बुरा है। बेस कक्षाओं को लगभग यह जानने की जरूरत है कि कौन इसे प्राप्त करने जा रहा है, और कितने अन्य बेस क्लास व्युत्पन्न होंगे, और किस क्रम में ... अन्यथा superया तो चलाने में विफल होंगे (पैरामीटर बेमेल के कारण), या यह कॉल नहीं करेगा कुछ ठिकानों (क्योंकि आपने आधार superमें से एक में नहीं लिखा था जो लिंक को तोड़ता है)!
नवाज

185

मैं उत्तर को थोड़ा बेजान करके विस्तृत करना चाहता था क्योंकि जब मैंने पायथन में एक बहु विरासत वंशानुक्रम में सुपर () का उपयोग करने के बारे में पढ़ना शुरू किया, तो मुझे तुरंत नहीं मिला।

आपको जो समझने की आवश्यकता है वह यह है कि पूर्ण विरासत पदानुक्रम के संदर्भ में प्रयुक्त विधि रिज़ॉल्यूशन ऑर्डरिंग (एमआरओ) एल्गोरिदम के अनुसार अगली विधि super(MyClass, self).__init__()प्रदान करता है __init__

यह अंतिम भाग समझने के लिए महत्वपूर्ण है। आइए फिर से उदाहरण पर विचार करें:

#!/usr/bin/env python2

class First(object):
  def __init__(self):
    print "First(): entering"
    super(First, self).__init__()
    print "First(): exiting"

class Second(object):
  def __init__(self):
    print "Second(): entering"
    super(Second, self).__init__()
    print "Second(): exiting"

class Third(First, Second):
  def __init__(self):
    print "Third(): entering"
    super(Third, self).__init__()
    print "Third(): exiting"

गुइडो वैन रोसुम द्वारा मेथड रेजोल्यूशन ऑर्डर के बारे में इस लेख के अनुसार , हल करने के लिए आदेश __init__की गणना की गई है (पायथन 2.3 से पहले) "गहराई-पहले बाएं से दाएं ट्रैवर्सल" का उपयोग करके:

Third --> First --> object --> Second --> object

सभी डुप्लिकेट को हटाने के बाद, पिछले एक को छोड़कर, हमें मिलता है:

Third --> First --> Second --> object

इसलिए, जब हम Thirdकक्षा की एक आवृत्ति का उदाहरण देते हैं, तब क्या होता है, इसका अनुसरण करते हैं x = Third()

  1. MRO के अनुसार Third.__init__निष्पादित करता है।
    • प्रिंट Third(): entering
    • फिर super(Third, self).__init__()निष्पादित और एमआरओ रिटर्न First.__init__जिसे कहा जाता है।
  2. First.__init__ निष्पादित करता है।
    • प्रिंट First(): entering
    • फिर super(First, self).__init__()निष्पादित और एमआरओ रिटर्न Second.__init__जिसे कहा जाता है।
  3. Second.__init__ निष्पादित करता है।
    • प्रिंट Second(): entering
    • फिर super(Second, self).__init__()निष्पादित और एमआरओ रिटर्न object.__init__जिसे कहा जाता है।
  4. object.__init__ निष्पादित (कोड में कोई प्रिंट स्टेटमेंट नहीं)
  5. निष्पादन वापस जाता है Second.__init__, फिर प्रिंट करता हैSecond(): exiting
  6. निष्पादन वापस जाता है First.__init__, फिर प्रिंट करता हैFirst(): exiting
  7. निष्पादन वापस जाता है Third.__init__, फिर प्रिंट करता हैThird(): exiting

यह बताता है कि क्यों तत्काल तीसरे () में परिणाम है:

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

जटिल मामलों में अच्छी तरह से काम करने के लिए एमआरओ एल्गोरिथ्म को पायथन 2.3 से बेहतर बनाया गया है, लेकिन मुझे लगता है कि "डेप्थ-फर्स्ट लेफ्ट-टू-राइट ट्रैवर्सल" + का उपयोग करके "अंतिम" के लिए डुप्लिकेट को हटाने की उम्मीद है "अभी भी ज्यादातर मामलों में काम करता है (कृपया अगर यह मामला नहीं है तो टिप्पणी करें)। गुइडो द्वारा ब्लॉग पोस्ट को पढ़ना सुनिश्चित करें!


6
मैं अब भी समझ में नहीं आता क्यों है: अंदर init पहले सुपर (प्रथम, आत्म) के .__ init __ () कॉल init , द्वितीय की वजह से है कि क्या एमआरओ तय है!
1938 पर user389955

@ user389955 बनाई गई वस्तु टाइप थर्ड की है जिसमें सभी init मेथड हैं। इसलिए यदि आप मानते हैं कि MRO एक विशिष्ट क्रम में सभी init फ़ंक्शनों की एक सूची बनाता है, तो हर सुपर कॉल के साथ, आप अंत तक पहुंचने तक एक कदम आगे जा रहे हैं।
श्रीकुमार आर।

15
मुझे लगता है कि चरण 3 को अधिक स्पष्टीकरण की आवश्यकता है: यदि आपको Thirdविरासत में नहीं मिला है Second, तो super(First, self).__init__कॉल करेगा object.__init__और लौटने के बाद, "पहले" मुद्रित किया जाएगा। लेकिन क्योंकि Thirdदोनों से विरासत में मिली Firstऔर Secondनहीं बल्कि बुला से, object.__init__के बाद First.__init__एमआरओ तय है कि करने के लिए केवल अंतिम कॉल object.__init__संरक्षित है, और में प्रिंट बयान Firstऔर Secondजब तक पहुँच नहीं कर रहे हैं object.__init__रिटर्न। चूंकि Secondकॉल करने का आखिरी समय था object.__init__, यह अंदर Secondलौटने से पहले अंदर जाता है First
MountainDrew

1
दिलचस्प बात यह है कि, Pyharm को यह सब पता चल रहा है (इसके संकेत इस बारे में बात करते हैं कि कौन से पैरामीटर सुपर के साथ कॉल करते हैं। इसमें इनपुट के सह-प्रसार की कुछ धारणा भी है, इसलिए यह मान लेता है List[subclass]कि List[superclass]क्या subclassयह सबक्लेश है superclass( PEP 483 Listके typingमॉड्यूल से आता है) iirc)।
Reb.Cabin

अच्छी पोस्ट है, लेकिन मुझे कंस्ट्रक्टर्स के तर्कों के संबंध में जानकारी याद आती है, अर्थात क्या होता है यदि दूसरा और पहला अलग-अलग तर्कों की अपेक्षा करता है? पहले के निर्माता को कुछ तर्कों को संसाधित करना होगा और बाकी को दूसरे के साथ पारित करना होगा। क्या वह सही है? यह मेरे लिए सही नहीं लगता है कि पहले दूसरे के लिए आवश्यक तर्कों के बारे में जानना चाहिए।
क्रिश्चियन के।

58

इसे डायमंड प्रॉब्लम के रूप में जाना जाता है , पेज में पायथन पर एक प्रविष्टि है, लेकिन संक्षेप में, पायथन सुपरक्लास के तरीकों को बाएं से दाएं पर कॉल करेगा।


यह डायमंड की समस्या नहीं है। डायमंड समस्या में चार वर्ग शामिल हैं और ओपी के प्रश्न में केवल तीन शामिल हैं।
इयान गुडफेलो

146
objectचौथा है
GP89 13

28

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

यहाँ Aएक बढ़ाई आधार वर्ग है और Bऔर CMixin कक्षाएं दोनों जो समारोह प्रदान कर रहे हैं fAऔर Bदोनों vअपने __init__और Cअपेक्षाओं में पैरामीटर की उम्मीद करते हैं w। फ़ंक्शन fएक पैरामीटर लेता है yQतीनों वर्गों से विरासत में मिली। MixInFके लिए Bऔर मिक्सिन इंटरफ़ेस है C


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)

मुझे लगता है कि यह शायद एक अलग सवाल-जवाब होना चाहिए, क्योंकि एमआरओ अपने आप में एक बड़ा पर्याप्त विषय है, जिसमें विरासत के साथ कार्यों में अलग-अलग तर्क दिए जा रहे हैं (एकाधिक विरासत उसी का एक विशेष मामला है)।
निर्जीव

8
सैद्धांतिक रूप से, हाँ। व्यावहारिक रूप से, यह परिदृश्य हर बार आया है जब मैंने अजगर में डायमंड इनहेरिटेंस का सामना किया है, इसलिए मैंने इसे यहां जोड़ा। चूंकि, यह वह जगह है जहां मैं हर बार जाता हूं, मैं हीरे की विरासत से सफाई नहीं कर सकता। यहाँ मुझे भविष्य के लिए कुछ अतिरिक्त लिंक दिए गए हैं: rhettinger.wordpress.com/2011/05/26/super-considered-super code.activestate.com/recipes/…
brent.payne

हम जो चाहते हैं वह शब्दार्थिक सार्थक नाम वाले कार्यक्रम हैं। लेकिन इस उदाहरण में लगभग सभी मापदंडों को गुमनाम रूप से नामित किया गया है, जो मूल प्रोग्रामर के लिए कोड का दस्तावेजीकरण करने और कोड को पढ़ने के लिए किसी अन्य प्रोग्रामर के लिए और अधिक कठिन बना देगा।
आर्थर

वर्णनात्मक नामों के साथ
गिटब

@ brent.payne मुझे लगता है कि @Arthur का मतलब है कि अपने पूरे दृष्टिकोण का उपयोग पर निर्भर करता है args/ kwargsनाम वाले पैरामीटर के बजाय।
अधिकतम

24

मैं समझता हूं कि यह सीधे सवाल का जवाब नहीं देता है super(), लेकिन मुझे लगता है कि यह साझा करने के लिए पर्याप्त प्रासंगिक है।

प्रत्येक विरासत वर्ग को सीधे कॉल करने का एक तरीका भी है:


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

बस ध्यान दें कि यदि आप इसे इस तरह से करते हैं, आप के रूप में मैं बहुत यकीन है कि मैन्युअल रूप से प्रत्येक कॉल करने के लिए होगा Firstके __init__()बुलाया नहीं किया जाएगा।


5
इसे इसलिए नहीं बुलाया जाएगा क्योंकि आपने प्रत्येक विरासत वाले वर्ग को नहीं बुलाया था। समस्या नहीं बल्कि यह है कि अगर है Firstऔर Secondदोनों एक और वर्ग इनहेरिट और सीधे तो यह सामान्य वर्ग (हीरे की प्रारंभिक बिंदु) यह बुला रहे हैं दो बार कहा जाता है। सुपर इससे बच रहा है।
ट्रिलियनियन

@ ट्रिलियन हाँ, मुझे विश्वास था कि यह नहीं होगा। हालाँकि, मुझे निश्चित रूप से पता नहीं था और मैं यह बताना नहीं चाहता था कि मैंने ऐसा किया है, हालांकि यह बहुत कम संभावना थी। यह objectदो बार कहा जा रहा है के बारे में एक अच्छी बात है । मैंने उसके बारे में नहीं सोचा। मैं सिर्फ यह बात बनाना चाहता था कि आप सीधे पैरेंट क्लासेस को बुलाएँ।
सीउक्स

दुर्भाग्य से, यह टूट जाता है अगर यह init किसी भी निजी तरीके को एक्सेस करने की कोशिश करता है :(
एरिक एरोनेस्टी

21

संपूर्ण

यह मानते हुए कि सब कुछ से उतरता है object(आप अपने दम पर अगर यह नहीं है), पायथन आपके वर्ग वंशानुक्रम के आधार पर एक विधि संकल्प आदेश (एमआरओ) की गणना करता है। MRO 3 गुणों को संतुष्ट करता है:

  • एक वर्ग के बच्चे अपने माता-पिता से पहले आ
  • बाएं माता-पिता सही माता-पिता से पहले आते हैं
  • एक वर्ग केवल एक बार MRO में दिखाई देता है

यदि ऐसा कोई आदेश मौजूद नहीं है, तो पायथन त्रुटियां। इसका आंतरिक कामकाज कक्षाओं के वंश का सी 3 लाइनराइजेशन है। इसके बारे में सब यहाँ पढ़ें: https://www.python.org/download/releases/2.3/mro/

इस प्रकार, नीचे दिए गए दोनों उदाहरणों में यह है:

  1. बच्चा
  2. बाएं
  3. सही
  4. माता-पिता

जब किसी विधि को कहा जाता है, तो MRO में उस विधि की पहली घटना वह होती है जिसे कहा जाता है। कोई भी वर्ग जो उस पद्धति को लागू नहीं करता है, उसे छोड़ दिया जाता है। superउस पद्धति के भीतर कोई भी कॉल उस विधि की अगली घटना को MRO में बुलाएगा। नतीजतन, यह दोनों मायने रखता है कि आप किस क्रम में वंशानुक्रम में कक्षाएं लगाते हैं, और जहां आप कॉल को superविधियों में रखते हैं।

superप्रत्येक विधि में पहले के साथ

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print "parent"

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print "left"

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print "right"

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print "child"

Child() आउटपुट:

parent
right
left
child

superप्रत्येक विधि में अंतिम के साथ

class Parent(object):
    def __init__(self):
        print "parent"
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print "left"
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print "right"
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print "child"
        super(Child, self).__init__()

Child() आउटपुट:

child
left
right
parent

मुझे लगता है कि आप उपयोग कर सकते हैं देखने Leftका उपयोग कर super()से Child। मान लीजिए मैं Rightअंदर से प्रवेश करना चाहता हूं Child। क्या सुपर का उपयोग करने Rightसे पहुंचने का एक तरीका है Child? या मुझे सीधे Rightअंदर से बुलाया जाना चाहिए super?
अल्फा_989

4
@ अल्फ़ा_989 यदि आप केवल किसी विशेष वर्ग की पद्धति का उपयोग करना चाहते हैं, तो आपको सुपर का उपयोग करने के बजाय सीधे उस वर्ग का संदर्भ देना चाहिए। सुपर विरासत की श्रृंखला के बाद, एक विशिष्ट वर्ग की विधि के लिए हो रही नहीं के बारे में है।
जैग्स

1
स्पष्ट रूप से 'A वर्ग केवल एक बार MRO में प्रकट होता है' का उल्लेख करने के लिए धन्यवाद। इससे मेरी समस्या हल हो गई। अब मैं अंत में समझता हूं कि एकाधिक वंशानुक्रम कैसे काम करता है। किसी को MRO के गुणों का उल्लेख करने की आवश्यकता है!
तुषार वज़ीरानी

18

@ बछड़े की टिप्पणी के बारे में , आप आमतौर पर, का उपयोग कर सकते हैं **kwargs:

ऑनलाइन चल रहा है उदाहरण

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

परिणाम:

A None
B hello
A1 6
B1 5

आप इनका उपयोग स्थिति में भी कर सकते हैं:

B1(5, 6, b="hello", a=None)

लेकिन आपको एमआरओ को याद रखना होगा, यह वास्तव में भ्रमित करने वाला है।

मैं थोड़ा परेशान हो सकता हूं, लेकिन मैंने देखा कि लोग हर बार इस्तेमाल करना भूल जाते हैं *argsऔर **kwargsजब वे एक विधि को ओवरराइड करते हैं, जबकि यह इन 'मैजिक वेरिएबल्स' के कुछ बहुत ही उपयोगी और समझदार उपयोगों में से एक है।


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

15

एक और नहीं अभी तक कवर बिंदु वर्गों के आरंभीकरण के लिए पैरामीटर पारित कर रहा है। चूंकि superउपवर्ग पर निर्भर करता है कि मापदंडों को पारित करने का एकमात्र अच्छा तरीका उन सभी को एक साथ पैक करना है। फिर अलग-अलग अर्थों के साथ एक ही पैरामीटर नाम नहीं होने के लिए सावधान रहें।

उदाहरण:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

देता है:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

सुपर क्लास को __init__सीधे मापदंडों के अधिक प्रत्यक्ष असाइनमेंट पर superकॉल करना लुभावना है लेकिन अगर सुपर क्लास में कोई कॉल है और / या एमआरओ को बदल दिया जाता है और कार्यान्वयन के आधार पर क्लास ए को कई बार कहा जा सकता है।

निष्कर्ष निकालने के लिए: प्रारंभ के लिए सहकारी विरासत और सुपर और विशिष्ट पैरामीटर एक साथ बहुत अच्छी तरह से काम नहीं कर रहे हैं।


5
class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

class Second(object):
  def __init__(self, a):
    print "second", a
    super(Second, self).__init__()

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__(10)
    print "that's it"

t = Third()

आउटपुट है

first 10
second 20
that's it

थर्ड को कॉल करें () थर्ड में परिभाषित इनिट को रेखांकित करता है । और उस रूटीन में सुपर को कॉल करें जो कि इनफिट इनफिट को फर्स्ट में परिभाषित करता है । एमआरओ = [पहला, दूसरा]। अब में सुपर करने के लिए कॉल init प्रथम में परिभाषित एमआरओ खोज जारी है और मिलेगा init दूसरा में परिभाषित किया गया है, और सुपर के लिए किसी भी कॉल डिफ़ॉल्ट वस्तु मारा जाएगा init । मुझे उम्मीद है कि यह उदाहरण अवधारणा को स्पष्ट करता है।

यदि आप पहले से सुपर फोन नहीं करते हैं। श्रृंखला बंद हो जाती है और आपको निम्न आउटपुट मिलेगा।

first 10
that's it

1
ऐसा इसलिए है क्योंकि कक्षा पहली में, आपने पहले 'प्रिंट' और फिर 'सुपर' कहा था।
रॉकी क्यूई

2
कॉलिंग ऑर्डर को स्पष्ट करना था
सेराज अहमद

4

Learningpythonthehardway में मैं सुपर () इन-बिल्ट फंक्शन नामक कुछ सीखता हूं यदि गलत नहीं है। सुपर () फ़ंक्शन को कॉल करने से विरासत को माता-पिता और 'भाई-बहनों' के माध्यम से पारित करने में मदद मिल सकती है और आपको स्पष्ट देखने में मदद मिल सकती है। मैं अभी भी एक शुरुआत कर रहा हूं, लेकिन मुझे इस सुपर () का उपयोग python2.7 में अपने अनुभव को साझा करने के लिए करना पसंद है।

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

सुपर () फ़ंक्शन को जोड़कर

super(First, self).__init__() #example for class First.

आप प्रत्येक में और सभी में जोड़कर, कई उदाहरणों और 'परिवारों' को सुपर () के साथ जोड़ सकते हैं। और यह विधियों को निष्पादित करेगा, उनके माध्यम से जाएं और सुनिश्चित करें कि आपने याद नहीं किया! हालाँकि, इससे पहले या बाद में उन्हें जोड़ने से आपको फ़र्क़ पड़ेगा कि आपको पता चल जाएगा कि आपने क्या किया है।

नीचे उदाहरण देते हुए, आप इसे कॉपी और पेस्ट कर सकते हैं और चलाने का प्रयास कर सकते हैं:

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()

कैसे चलता है? पाँचवाँ () का उदाहरण इस प्रकार होगा। प्रत्येक चरण कक्षा से कक्षा तक जाता है जहां सुपर फ़ंक्शन जोड़ा गया है।

1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

जनक पाया गया और यह तीसरी और चौथी तक जारी रहेगा !!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

अब सुपर () के साथ सभी वर्गों तक पहुँचा गया है! मूल वर्ग पाया और निष्पादित किया गया है और अब यह कोड समाप्त करने के लिए विरासत में फ़ंक्शन को अनबॉक्स करना जारी रखता है।

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

उपरोक्त कार्यक्रम का परिणाम:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

मेरे लिए सुपर जोड़कर () मुझे यह स्पष्ट करने की अनुमति देता है कि अजगर मेरे कोडिंग को कैसे निष्पादित करेगा और यह सुनिश्चित करेगा कि वंशानुक्रम मेरी इच्छित विधि तक पहुंच सकता है।


विस्तृत डेमो के लिए धन्यवाद!
तुषार वज़ीरानी

3

मैं जोड़ना चाहूंगा कि @Visioncaper शीर्ष पर क्या कहता है :

Third --> First --> object --> Second --> object

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

वर्ग C, L (C) का रैखिककरण (mro), है

  • वर्ग सी
  • से अधिक का विलय
    • अपने माता-पिता पी 1, पी 2, .. = एल (पी 1, पी 2, ...) के रैखिककरण
    • इसके माता-पिता पी 1, पी 2, की सूची।

रेखीयकृत मर्ज उन सामान्य वर्गों का चयन करके किया जाता है जो सूचियों के प्रमुख के रूप में प्रकट होते हैं न कि आदेश मामलों के बाद से पूंछ के रूप में (नीचे स्पष्ट हो जाएगा)

तीसरे के रैखिककरण की गणना निम्नानुसार की जा सकती है:

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

इस प्रकार निम्नलिखित कोड में एक सुपर () कार्यान्वयन के लिए:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

यह स्पष्ट हो जाता है कि इस विधि को कैसे हल किया जाएगा

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"

"बल्कि इसकी वजह है कि दूसरा एक सिर की स्थिति में दिखाई देता है और एक पदानुक्रम उपसमुच्चय में पूंछ की स्थिति में प्रकट नहीं होता है।" यह स्पष्ट नहीं है कि एक सिर या पूंछ की स्थिति क्या है, न ही एक पदानुक्रम सबसेट क्या है या आप कौन सा सबसेट का उल्लेख कर रहे हैं।
ऑरेंजशेरबेट

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

3

अजगर में 3.5+ वंशानुक्रम मेरे लिए अनुमानित और बहुत अच्छा लगता है। कृपया इस कोड को देखें:

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

आउटपुट:

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

जैसा कि आप देख सकते हैं, यह प्रत्येक विरासत में मिली श्रृंखला के लिए ठीक उसी समय को फू कहता है, जैसा कि उसे विरासत में मिला था। आप उस आदेश को कॉल करके प्राप्त कर सकते हैं mro :

चौथा -> तीसरा -> पहला -> दूसरा -> आधार -> वस्तु


2

शायद वहाँ अभी भी कुछ है कि जोड़ा जा सकता है, Django rest_framework, और सज्जाकार के साथ एक छोटा सा उदाहरण। यह निहित प्रश्न का उत्तर प्रदान करता है: "मैं वैसे भी यह क्यों चाहूंगा?"

जैसा कि कहा गया: हम Django rest_framework के साथ हैं, और हम सामान्य विचारों का उपयोग कर रहे हैं, और हमारे डेटाबेस में प्रत्येक प्रकार की वस्तुओं के लिए हम खुद को एक दृश्य वर्ग के साथ पाते हैं जो वस्तुओं की सूचियों के लिए GET और POST प्रदान करते हैं, और एक अन्य दृश्य वर्ग GET प्रदान करते हैं। , व्यक्तिगत वस्तुओं के लिए, और DELETE।

अब POST, PUT, और DELETE को हम Django के login_required से सजाना चाहते हैं। ध्यान दें कि यह दोनों वर्गों को कैसे छूता है, लेकिन दोनों तरीकों को सभी वर्गों में नहीं।

एक समाधान कई विरासत के माध्यम से जा सकता है।

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

इसी तरह अन्य तरीकों के लिए।

मेरे ठोस वर्गों की विरासत सूची में, मैं अपने LoginToPostपहले ListCreateAPIViewऔर LoginToPutOrDeleteपहले जोड़ूंगा RetrieveUpdateDestroyAPIView। मेरा ठोस वर्ग ' getअनिर्णीत रहेगा।


1

मेरे भविष्य के संदर्भ के लिए इस उत्तर को पोस्ट करना।

पायथन मल्टीपल इनहेरिटेंस को एक हीरे के मॉडल का उपयोग करना चाहिए और फ़ंक्शन हस्ताक्षर को मॉडल में नहीं बदलना चाहिए।

    A
   / \
  B   C
   \ /
    D

नमूना कोड स्निपेट होगा ;;

class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')

यहाँ क्लास ए है object


1
Aचाहिए भी बुला हो __init__Aविधि का "आविष्कार" नहीं किया __init__, इसलिए यह नहीं मान सकते हैं कि कुछ अन्य वर्ग Aपहले अपने एमआरओ में हो सकते हैं । एकमात्र वर्ग जिसकी __init__पद्धति कॉल नहीं करती है (और नहीं होनी चाहिए) super().__init__है object
चेपनर

हाँ। इसीलिए मैंने A लिखा है, objectशायद मुझे लगता है, मुझे class A (object) : इसके बजाय लिखना चाहिए
अखिल नाद पीसी

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