पायथन में do सुपर ’क्या करता है?


563

के बीच क्या अंतर है:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

तथा:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

मैंने देखा है superकि केवल एकल वंशानुक्रम के साथ कक्षाओं में काफी उपयोग किया जा रहा है । मैं देख सकता हूं कि आप इसका उपयोग कई उत्तराधिकार में क्यों करेंगे लेकिन इस तरह की स्थिति में इसका उपयोग करने के क्या फायदे हैं, यह स्पष्ट नहीं है।

जवाबों:


308

के फायदे है super()सिंगल-इनहेरिटेंस के कम से कम हैं - ज्यादातर, आपको बेस क्लास के नाम को हर विधि में हार्ड-कोड करने की ज़रूरत नहीं है जो इसके मूल तरीकों का उपयोग करता है।

हालांकि, इसके बिना कई-विरासत का उपयोग करना लगभग असंभव है super()। इसमें मिश्रण, इंटरफेस, अमूर्त वर्ग इत्यादि जैसे सामान्य मुहावरे शामिल हैं। यह उन कोडों तक फैली हुई है जो बाद में आपका विस्तार करते हैं। यदि कोई बाद में एक वर्ग को लिखना चाहता है जो विस्तारित Childऔर एक मिश्रण है, तो उनका कोड ठीक से काम नहीं करेगा।


6
क्या आप इसका उदाहरण दे सकते हैं कि आपका क्या मतलब है "यह ठीक से काम नहीं करेगा"?
चार्ली पार्कर

319

क्या फर्क पड़ता है?

SomeBaseClass.__init__(self) 

कॉल करने के लिए साधन SomeBaseClass'एस __init__। जबकि

super(Child, self).__init__()

उदाहरण के विधि संकल्प क्रम (MRO) में __init__पालन ​​करने वाले मूल वर्ग से एक बाउंड को कॉल करने का मतलब है Child

यदि उदाहरण बाल का उपवर्ग है, तो MRO में एक अलग अभिभावक हो सकता है।

सरलता से समझाया गया

जब आप एक कक्षा लिखते हैं, तो आप चाहते हैं कि अन्य वर्ग इसका उपयोग करने में सक्षम हों। super()अन्य वर्गों के लिए आपके द्वारा लिखी गई कक्षा का उपयोग करना आसान बनाता है।

जैसा कि बॉब मार्टिन कहते हैं, एक अच्छा आर्किटेक्चर आपको यथासंभव लंबे समय तक निर्णय लेने को स्थगित करने की अनुमति देता है।

super() आर्किटेक्चर के उस प्रकार को सक्षम कर सकता है।

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

बिना superआपके द्वारा लिखे गए कक्षा के माता-पिता के लिए कठिन-कोड की संभावना होगी (जैसे कि उदाहरण करता है)। इसका मतलब यह होगा कि आप __init__एमआरओ में अगले को नहीं बुलाएंगे, और इस प्रकार आपको इसमें कोड का पुन: उपयोग नहीं करना पड़ेगा।

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

अजगर 2 बनाम 3

यह पायथन 2 और 3 में काम करता है:

super(Child, self).__init__()

यह केवल पायथन 3 में काम करता है:

super().__init__()

यह स्टैक फ्रेम में आगे बढ़ने और विधि के लिए पहला तर्क प्राप्त करने के बिना कोई तर्क के साथ काम करता है (आमतौर पर selfएक उदाहरण विधि के लिए या clsएक वर्ग विधि के लिए - लेकिन अन्य नाम हो सकता है) और Childमुक्त चर में वर्ग (जैसे ) खोज रहा है ( यह नाम के साथ देखा __class__जाता है विधि में एक मुक्त बंद चर के रूप में)।

मैं उपयोग करने के क्रॉस-संगत तरीके का प्रदर्शन करना पसंद करता हूं super, लेकिन यदि आप केवल पायथन 3 का उपयोग कर रहे हैं, तो आप इसे बिना किसी तर्क के कह सकते हैं।

आगे की संगतता के साथ अप्रत्यक्ष

यह आपको क्या देता है? एकल वंशानुक्रम के लिए, प्रश्न के उदाहरण व्यावहारिक रूप से स्थिर विश्लेषण बिंदु से समान हैं। हालाँकि, उपयोग करने superसे आपको आगे की संगतता के साथ अप्रत्यक्ष की एक परत मिलती है।

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

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

निर्भरता अन्तःक्षेपण

अन्य लोग आपके कोड का उपयोग कर सकते हैं और माता-पिता को विधि समाधान में इंजेक्ट कर सकते हैं:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

कहते हैं कि आप अपनी वस्तु में एक और वर्ग जोड़ते हैं, और फू और बार (परीक्षण या किसी अन्य कारण से) के बीच एक वर्ग इंजेक्ट करना चाहते हैं:

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

संयुक्त राष्ट्र के सुपर-बच्चे का उपयोग निर्भरता को इंजेक्ट करने में विफल रहता है क्योंकि आप जिस बच्चे का उपयोग कर रहे हैं, उसने हार्ड-कोडित विधि को अपने स्वयं के बाद कहा जाता है:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

हालाँकि, बच्चे का उपयोग करने वाला वर्ग superसही ढंग से निर्भरता को इंजेक्ट कर सकता है:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

एक टिप्पणी को संबोधित करते हुए

क्यों दुनिया में यह उपयोगी होगा?

पायथन C3 रैखिककरण एल्गोरिथ्म के माध्यम से एक जटिल वंशानुक्रम वृक्ष को रेखीय करता है एक विधि रिज़ॉल्यूशन ऑर्डर (MRO) बनाने के है।

हम चाहते हैं कि तरीकों को उसी क्रम में देखा जाए

उस क्रम में अगले एक को खोजने के लिए एक माता-पिता में परिभाषित विधि के लिए super, यह करना होगा

  1. उदाहरण के प्रकार से mro प्राप्त करें
  2. उस प्रकार की तलाश करें जो विधि को परिभाषित करता है
  3. विधि के साथ अगला प्रकार ढूंढें
  4. उस पद्धति को बांधें और अपेक्षित तर्कों के साथ इसे कॉल करें

UnsuperChildके लिए उपयोग नहीं करना चाहिए था InjectMe। निष्कर्ष "हमेशा उपयोग करने से बचें" क्यों नहीं है super? मुझे यहां क्या समझ नहीं आ रहा है?

UnsuperChildकरता नहीं की पहुंच है InjectMe। यह UnsuperInjectorउस तक पहुंच है InjectMe- और अभी तक उस वर्ग की विधि को उस विधि से नहीं कह सकता है जो इसे विरासत में मिली है UnsuperChild

दोनों बाल वर्ग एक ही नाम से एक विधि को कॉल करने का इरादा रखते हैं जो एमआरओ में आगे आता है, जो एक और वर्ग हो सकता है जब यह बनाया गया था, तो इसके बारे में पता नहीं था।

एक बिना super हार्ड-कोड के अपने माता-पिता की विधि - इस प्रकार इसकी पद्धति के व्यवहार को प्रतिबंधित कर दिया गया है, और उपवर्ग कॉल श्रृंखला में कार्यक्षमता को इंजेक्ट नहीं कर सकते हैं।

एक साथ super अधिक से अधिक लचीलेपन है। विधियों के लिए कॉल श्रृंखला इंटरसेप्ट की जा सकती है और कार्यक्षमता इंजेक्ट की जा सकती है।

आपको उस कार्यक्षमता की आवश्यकता नहीं हो सकती है, लेकिन आपके कोड के उप-वर्ग हो सकते हैं।

निष्कर्ष

हमेशा उपयोग करें super हार्ड-कोडिंग के बजाय मूल वर्ग का संदर्भ देने के लिए करें।

आप जो इरादा रखते हैं, वह मूल वर्ग का संदर्भ देना है जो अगली पंक्ति में है, विशेष रूप से वह नहीं जिसे आप बच्चे को विरासत में देखते हैं।

उपयोग न करने superसे आपके कोड के उपयोगकर्ताओं पर अनावश्यक अड़चनें आ सकती हैं।


C में, DI इस तरह है । कोड यहाँ है । यदि मैं listइंटरफ़ेस के एक और कार्यान्वयन को जोड़ता हूं , doublylinkedlistतो कहें कि एप्लिकेशन आसानी से इसे चुनता है। मैं config.txtलोड समय पर कार्यान्वयन और लिंक को लागू करके अपने उदाहरण को और अधिक विन्यास कर सकता हूं । क्या यह सही उदाहरण है? यदि हाँ, तो मैं आपके कोड से कैसे संबंधित करूँ? विकी में DI का पहला सलाह देखें। कोई नया कार्यान्वयन कॉन्फ़िगर करने योग्य कहाँ है? अपने कोड में
ओवरएक्सचेंज करें

एक नया कार्यान्वयन विरासत के माध्यम से बनाया गया है, उदाहरण के लिए, जहां "इंजेक्टर" वर्गों में से एक InjectMeवर्ग से विरासत में मिला है । टिप्पणियाँ चर्चा के लिए नहीं हैं, फिर भी, मेरा सुझाव है कि आप दूसरों से इस बारे में चर्चा करें या मुख्य साइट पर एक नया प्रश्न पूछें।
हारून हॉल

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

35

मैंने थोड़ा सा साथ निभाया था super() , और माना था कि हम कॉलिंग ऑर्डर बदल सकते हैं।

उदाहरण के लिए, हमारे पास अगली पदानुक्रम संरचना है:

    A
   / \
  B   C
   \ /
    D

इस स्थिति में D का MRO होगा (केवल पायथन 3 के लिए):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

चलो एक वर्ग बनाते हैं जहां super()विधि निष्पादन के बाद कॉल होता है।

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / 
  B  C
    /
    D

इसलिए हम देख सकते हैं कि रिज़ॉल्यूशन ऑर्डर MRO के समान है। लेकिन जब हम super()विधि की शुरुआत में कहते हैं :

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

हमारे पास एक अलग आदेश है यह एमआरओ टपल के एक आदेश के विपरीत है।

    A
   / 
  B  C
    /
    D 

अतिरिक्त पढ़ने के लिए मैं अगले उत्तरों की सिफारिश करूंगा:

  1. सुपर (एक बड़ी पदानुक्रम) के साथ C3 रैखिककरण उदाहरण
  2. पुरानी और नई शैली की कक्षाओं के बीच महत्वपूर्ण व्यवहार बदलता है
  3. न्यू-स्टाइल क्लासेस पर इनसाइड स्टोरी

मुझे समझ नहीं आ रहा है कि आदेश क्यों बदल रहा है। पहला भाग मैं समझता हूँ कि DBCA क्योंकि D प्रथम श्रेणी है, फिर जब सेल्फ लोड (B, C) अंततः B, C को प्रिंट करेगा, तब से केवल A से B (A), C (A) ने फ़ाइनल के लिए वापस जाने का संकेत दिया अंश। अगर मैं इस समझ का पालन करूं, तो दूसरा हिस्सा बीसीएडी की तरह नहीं होना चाहिए? क्या आप कृपया मुझे थोड़ा समझा सकते हैं।
JJson

मेरा बुरा, मैंने ध्यान नहीं दिया कि प्रत्येक वर्ग उदाहरण को सुपर () के साथ शुरू किया गया है। फिर अगर ऐसा है, तो इसे ABCD नहीं होना चाहिए? मैं किसी तरह समझता हूँ कि ACBD कैसे आया लेकिन फिर भी मना नहीं सका और अभी भी थोड़ा भ्रमित है। मेरी समझ यह है कि, d = D () को कक्षा D (B, C) कहा जाता है, 2 स्व-मापदंडों के साथ, क्योंकि सुपर () को पहले शुरू किया जाता है, फिर B को एक साथ बुलाया जाता है, इसके गुण तब D को C से पहले मुद्रित नहीं किया जाता है क्योंकि वर्ग D (B, C) में 2 स्व-पैरामीटर हैं, इसलिए इसे दूसरे को निष्पादित करना होगा जो कि क्लास C (A) है, जिसे निष्पादित करने के बाद निष्पादित करने के लिए अधिक स्व-पैरामीटर नहीं है
JJson

यह mro परिभाषा पर आधारित है ।
SKhalymon

1
तो यह C प्रिंट करेगा फिर B प्रिंट करेगा और अंत में D. I I प्रिंट करेगा?
JJson

2
जब तक आप पहली बार प्राप्त करते हैं तब तक दूसरे को समझना बहुत आसान है। यह एक ढेर की तरह है। आप स्टैक में प्रिंट '' को पुश करते हैं और सुपर करते हैं (), जब यह ए किया जाता है, तो यह उस स्टैक में चीजों को प्रिंट करना शुरू कर देता है, इसलिए ऑर्डर रिवर्स है।
सूर्य

35

क्या यह सब यह नहीं मानते कि आधार वर्ग एक नई शैली की कक्षा है?

class A:
    def __init__(self):
        print("A.__init__()")

class B(A):
    def __init__(self):
        print("B.__init__()")
        super(B, self).__init__()

पायथन 2 में काम नहीं करेगा। class Aनई शैली का होना चाहिए, अर्थात:class A(object)


20

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

एक वर्ग पदानुक्रम पर विचार करें A , Bऔर Cजहां प्रत्येक वर्ग यह निम्न में से एक की मूल है, और a, bऔर cप्रत्येक के संबंधित उदाहरणों।

super(B, b) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to b, as if b was an instance of A

super(C, c) 
# resolves to the scope of C's parent i.e. B
# and applies that scope to c

super(B, c) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to c

का उपयोग करते हुए superएक staticmethod के साथ

जैसे super()कि __new__()विधि के भीतर से उपयोग करना

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return super(A, cls).__new__(cls, *a, **kw)

स्पष्टीकरण:

1- भले ही यह सामान्य __new__()रूप से कॉलिंग क्लास के संदर्भ में अपना पहला परम लेने के लिए है, यह है पायथन में एक क्लासमेथोड के रूप में लागू नहीं किया गया है, बल्कि एक स्टेथमिथोड है। अर्थात, __new__()सीधे कॉल करते समय किसी कक्षा के संदर्भ को पहले तर्क के रूप में स्पष्ट रूप से पारित किया जाना चाहिए :

# if you defined this
class A(object):
    def __new__(cls):
        pass

# calling this would raise a TypeError due to the missing argument
A.__new__()

# whereas this would be fine
A.__new__(A)

2- super()अभिभावक वर्ग को प्राप्त करने के लिए कॉल करते समय हम बच्चे की कक्षा पास करते हैंA तो हम को उसके पहले तर्क के रूप में , फिर हम रुचि की वस्तु के संदर्भ में पास करते हैं, इस मामले में यह कक्षा का संदर्भ है A.__new__(cls)जिसे तब बुलाया गया था। ज्यादातर मामलों में यह बाल वर्ग के संदर्भ में भी होता है। उदाहरण के लिए, कुछ स्थितियों में यह कई पीढ़ी की विरासत के मामले में नहीं हो सकता है।

super(A, cls)

3- चूँकि एक सामान्य नियम __new__()एक स्थैतिक तत्व है,super(A, cls).__new__ को भी लौटाएगा और इस मामले में स्पष्ट रूप से इंस्ट्रस्ट की वस्तु के संदर्भ सहित सभी तर्कों की आपूर्ति की जानी चाहिए cls

super(A, cls).__new__(cls, *a, **kw)

4- बिना बात के एक ही काम करना super

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return object.__new__(cls, *a, **kw)

superएक इंस्टेंस विधि का उपयोग करना

जैसे super()भीतर से उपयोग कर रहे हैं__init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        super(A, self).__init__(*a, **kw)

स्पष्टीकरण:

1- __init__एक उदाहरण विधि है, जिसका अर्थ है कि यह एक उदाहरण के संदर्भ में अपने पहले तर्क के रूप में लेता है। जब उदाहरण से सीधे कॉल किया जाता है, तो संदर्भ को स्पष्ट रूप से पारित किया जाता है, अर्थात क्या आपको इसे निर्दिष्ट करने की आवश्यकता नहीं है:

# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...

# you create an instance
a = A()

# you call `__init__()` from that instance and it works
a.__init__()

# you can also call `__init__()` with the class and explicitly pass the instance 
A.__init__(a)

2- जब हम कॉल करते super()हैं __init__()तो हम बच्चे के वर्ग को पहले तर्क के रूप में और दूसरे तर्क के रूप में ब्याज की वस्तु के रूप में पास करते हैं, जो सामान्य रूप से बाल वर्ग के उदाहरण के लिए एक संदर्भ है।

super(A, self)

3- कॉल super(A, self)एक प्रॉक्सी देता है जो दायरे को हल करेगा और इसे लागू करेगा selfजैसे कि यह अब मूल वर्ग का एक उदाहरण है। चलो उस प्रॉक्सी को कॉल करें s। चूंकि __init__()एक इंस्टेंस मेथड है, इसलिए कॉल मूल रूप से माता-पिता के पहले तर्क के s.__init__(...)संदर्भ को पारित करेगाself__init__()

4- बिना superमाता-पिता के संस्करण के स्पष्ट रूप से उदाहरण के संदर्भ में जाने के लिए हमें ऐसा करने की आवश्यकता है __init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        object.__init__(self, *a, **kw)

superक्लासमैथोड के साथ उपयोग करना

class A(object):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        print "A.alternate_constructor called"
        return cls(*a, **kw)

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return super(B, cls).alternate_constructor(*a, **kw)

स्पष्टीकरण:

1- एक क्लासमेथोड को सीधे क्लास से बुलाया जा सकता है और क्लास के संदर्भ में इसके पहले पैरामीटर के रूप में लिया जाता है।

# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()

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

super(B, cls_or_subcls)

3- कॉल super(B, cls)के दायरे में हल Aहोता है और इसे लागू होता है cls। चूंकि alternate_constructor()एक क्लासमेथोड है, इसलिए कॉल पहले के संस्करण के पहले तर्क super(B, cls).alternate_constructor(...)के clsरूप में एक संदर्भ पारित करेगाAalternate_constructor()

super(B, cls).alternate_constructor()

4- बिना उपयोग के super()भी ऐसा करने के लिए आपको अनबाउंड वर्जन A.alternate_constructor()(यानी फंक्शन का स्पष्ट वर्जन) का संदर्भ लेना होगा। बस ऐसा करने से काम नहीं चलेगा:

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return A.alternate_constructor(cls, *a, **kw)

ऊपर काम नहीं करेगा क्योंकि A.alternate_constructor()विधि Aअपने पहले तर्क के रूप में एक अंतर्निहित संदर्भ लेती है। यहां clsसे पारित होना इसका दूसरा तर्क होगा।

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        # first we get a reference to the unbound 
        # `A.alternate_constructor` function 
        unbound_func = A.alternate_constructor.im_func
        # now we call it and pass our own `cls` as its first argument
        return unbound_func(cls, *a, **kw)

6

कई महान जवाब, लेकिन दृश्य शिक्षार्थियों के लिए: सबसे पहले सुपर के लिए तर्कों के साथ पता लगाने देता है, और फिर बिना। सुपर वंशानुक्रम वृक्ष उदाहरण

कल्पना करें jackकि कक्षा से निर्मित एक उदाहरण है Jack, जिसकी विरासत श्रृंखला है जैसा कि चित्र में हरे रंग में दिखाया गया है। कॉलिंग:

super(Jack, jack).method(...)

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

यदि कोई सुपर को तर्कों की आपूर्ति नहीं करता है, तो इसका पहला तर्क पास किया गया वर्ग है self, और दूसरा तर्क पास में है self। ये आपके लिए पायथन 3 में ऑटो-कैलकुलेट किए गए हैं।

हालाँकि हम कहते हैं कि हम इस Jackपद्धति का उपयोग नहीं करना चाहते हैं , इसके बजाय Jack, हम Jenइस पद्धति के लिए ऊपर की ओर खोज शुरू करने के लिए पारित हो सकते हैं ।Jen

यह एक समय (चौड़ाई नहीं गहराई) पर एक परत खोजता है, जैसे कि अगर Adamऔर Sueदोनों में आवश्यक विधि है, तो एक सेSue पहले वाला पाया जाएगा।

यदि Cainऔर Sueदोनों के लिए आवश्यक विधि थी, तो Cainपहले विधि को बुलाया जाएगा। यह कोड से मेल खाती है:

Class Jen(Cain, Sue):

MRO बाएं से दाएं होता है।


2

यहाँ कुछ महान जवाब हैं, लेकिन वे super()इस मामले में उपयोग करने के तरीके से नहीं निपटते हैं जहां पदानुक्रम में विभिन्न वर्गों के अलग-अलग हस्ताक्षर हैं ... विशेष रूप से इस मामले में__init__

उस भाग का उत्तर देने और प्रभावी ढंग से उपयोग करने में सक्षम होने के लिए super()मैं अपना उत्तर सुपर () पढ़ने और सहकारी विधियों के हस्ताक्षर को बदलने का सुझाव दूंगा ।

यहाँ इस परिदृश्य का समाधान है:

  1. अपनी पदानुक्रम में शीर्ष-स्तरीय कक्षाओं को एक कस्टम वर्ग से विरासत में प्राप्त करना होगा SuperObject:
  2. यदि कक्षाएं अलग-अलग तर्क ले सकती हैं, तो सुपर तर्क पर प्राप्त सभी तर्कों को हमेशा कीवर्ड तर्क के रूप में पास करें, और, हमेशा स्वीकार करें **kwargs
class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

उदाहरण का उपयोग:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

उत्पादन:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

0
class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

यह समझना काफी आसान है।

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

ठीक है, यदि आप उपयोग करते हैं तो अब क्या होता है super(Child,self)?

जब एक चाइल्ड इंस्टेंस बनाया जाता है, तो उसका MRO (मेथड रेजोल्यूशन ऑर्डर) इनहेरिटेंस के आधार पर (चाइल्ड, समबेसक्लेस, ऑब्जेक्ट) के क्रम में होता है। (मान लें कि कुछ ब्लेसक्लैस में डिफ़ॉल्ट ऑब्जेक्ट को छोड़कर अन्य माता-पिता नहीं हैं)

गुजरते हुए Child, self, उदाहरण superके MRO में खोज करता है self, और चाइल्ड के आगे प्रॉक्सी ऑब्जेक्ट को लौटाता है, इस मामले में यह SomeBaseClass है, यह ऑब्जेक्ट तब __init__SomeBaseClass की विधि को आमंत्रित करता है । दूसरे शब्दों में, यदि यह है super(SomeBaseClass,self), तो प्रॉक्सी ऑब्जेक्ट जो superरिटर्न करता हैobject

मल्टी इनहेरिटेंस के लिए, MRO में कई वर्ग हो सकते हैं, इसलिए मूल रूप superसे आपको यह तय करने देता है कि आप MRO में कहाँ से खोज करना शुरू करना चाहते हैं।


0

निम्नलिखित कोड पर विचार करें:

class X():
    def __init__(self):
        print("X")

class Y(X):
    def __init__(self):
        # X.__init__(self)
        super(Y, self).__init__()
        print("Y")

class P(X):
    def __init__(self):
        super(P, self).__init__()
        print("P")

class Q(Y, P):
    def __init__(self):
        super(Q, self).__init__()
        print("Q")

Q()

तो के परिवर्तन निर्माता Yके लिए X.__init__, आप मिल जाएगा:

X
Y
Q

लेकिन उपयोग करते हुए super(Y, self).__init__(), आपको मिलेगा:

X
P
Y
Q

और Pया Qयहां तक कि एक और फ़ाइल जो आप नहीं जानते कि जब आप लिखने से शामिल हो सकता है Xऔर Y। इसलिए, मूल रूप से, आपको नहीं पता super(Child, self)होगा कि जब आप लिख रहे हैं तो क्या संदर्भ होगा class Y(X), यहां तक ​​कि वाई के हस्ताक्षर भी उतने ही सरल हैं Y(X)। इसलिए सुपर एक बेहतर विकल्प हो सकता है।

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