super और multiple inheritance
multiple inheritance और super में जाने से पहले... चेतावनी, यह काफ़ी अजीब और जटिल हो सकता है।
सबसे पहले, multiple inheritance क्या है? अब तक के उदाहरण कोड में एक single child क्लास को एक single parent क्लास से inherit करते दिखाया गया है। multiple inheritance में एक से अधिक parent क्लास होते हैं। एक child क्लास 2, 3, 10, आदि parent क्लासेस से inherit कर सकती है।
यहीं पर super के लाभ अधिक स्पष्ट होते हैं। अलग-अलग parent क्लास नामों का संदर्भ देने के कीस्ट्रोक्स बचाने के अलावा, multiple inheritance पैटर्न के साथ super का उपयोग करने के सूक्ष्म लाभ हैं। संक्षेप में, यदि आप multiple inheritance का उपयोग करने जा रहे हैं, तो super का उपयोग करें।
super के बिना multiple inheritance
आइए multiple inheritance का एक उदाहरण देखते हैं जो किसी भी parent मेथड को संशोधित करने से बचता है और इस प्रकार super से भी बचता है।
Input
class B:
def b(self):
print('b')
class C:
def c(self):
print('c')
class D(B, C):
def d(self):
print('d')
d = D()
d.b()
d.c()
d.d()
Output
b
c
d
Multiple-resolution order
multiple inheritance की अवधारणा को देखते हुए यह आउटपुट बहुत चौंकाने वाला नहीं है। D ने अपने parent क्लासेस से x और z मेथड्स inherit किए, और अभी तक सब ठीक है...
तो क्या होगा अगर B और C दोनों के पास एक ही नाम का मेथड हो? यही वह जगह है जहाँ 'multiple-resolution order' या संक्षेप में MRO नामक अवधारणा काम में आती है। किसी child क्लास का MRO यह तय करता है कि Python किसी दिए गए मेथड के लिए कहाँ देखेगा, और जब टकराव होगा तो कौन सा मेथड कॉल होगा।
आइए एक उदाहरण देखें।
Input
class B:
def x(self):
print('x: B')
class C:
def x(self):
print('x: C')
class D(B, C):
pass
d = D()
d.x()
print(D.mro())
Output
x: B
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
जब हम inherited x मेथड को कॉल करते हैं, तो हमें केवल B से इनहेरिट हुआ आउटपुट दिखाई देता है। हम mro क्लास मेथड को कॉल करके अपनी D क्लास का MRO देख सकते हैं। D.mro() के आउटपुट से हम निम्न सीखते हैं: हमारा प्रोग्राम डिफ़ॉल्ट रूप से D के मेथड्स कॉल करने की कोशिश करेगा, फिर B, फिर C, और अंत में object। यदि यह कहीं भी नहीं मिला, तो हमें यह त्रुटि मिलेगी कि D में वह मेथड मौजूद नहीं है जिसे हमने माँगा था।
ध्यान देने योग्य है कि डिफ़ॉल्ट रूप से हर क्लास object से इनहेरिट करती है, और यह हर MRO के अंत में होता है।
Multiple inheritance, super, और diamond problem
नीचे init के MRO को लाभकारी तरीके से संभालने के लिए super के उपयोग का एक उदाहरण दिया गया है। उदाहरण में, हम टेक्स्ट प्रोसेसिंग की एक श्रृंखला की क्लासेस बनाते हैं और उनकी कार्यक्षमता को multiple inheritance के साथ दूसरी क्लास में संयोजित करते हैं। हम 4 क्लासेस बनाएँगे, और inheritance की संरचना नीचे दिए गए डायग्राम की संरचना का पालन करेगी।
नोट: यह संरचना केवल उदाहरण के लिए है, और यदि कोई बाधाएँ न हों, तो इस लॉजिक को लागू करने के बेहतर तरीके मौजूद होंगे।

यह वास्तव में multiple inheritance के 'diamond problem' का एक उदाहरण है। इसका नाम निश्चित रूप से इसकी डिज़ाइन की आकृति और इस तथ्य पर आधारित है कि यह काफ़ी पेचीदा समस्या है।
नीचे इस डिज़ाइन को super के उपयोग के साथ लिखा गया है।
Input
class Tokenizer:
"""Tokenize text"""
def __init__(self, text):
print('Start Tokenizer.__init__()')
self.tokens = text.split()
print('End Tokenizer.__init__()')
class WordCounter(Tokenizer):
"""Count words in text"""
def __init__(self, text):
print('Start WordCounter.__init__()')
super().__init__(text)
self.word_count = len(self.tokens)
print('End WordCounter.__init__()')
class Vocabulary(Tokenizer):
"""Find unique words in text"""
def __init__(self, text):
print('Start init Vocabulary.__init__()')
super().__init__(text)
self.vocab = set(self.tokens)
print('End init Vocabulary.__init__()')
class TextDescriber(WordCounter, Vocabulary):
"""Describe text with multiple metrics"""
def __init__(self, text):
print('Start init TextDescriber.__init__()')
super().__init__(text)
print('End init TextDescriber.__init__()')
td = TextDescriber('row row row your boat')
print('--------')
print(td.tokens)
print(td.vocab)
print(td.word_count)
Output
Start init TextDescriber.__init__()
Start WordCounter.__init__()
Start init Vocabulary.__init__()
Start Tokenizer.__init__()
End Tokenizer.__init__()
End init Vocabulary.__init__()
End WordCounter.__init__()
End init TextDescriber.__init__()
--------
['row', 'row', 'row', 'your', 'boat']
{'boat', 'your', 'row'}
5
सबसे पहले, हम देखते हैं कि TextDescriber क्लास ने क्लास फैमिली ट्री के सभी attributes inherit कर लिए हैं। multiple inheritance की बदौलत हम एक से अधिक क्लास की कार्यक्षमता को 'कंबाइन' कर सकते हैं।
अब उन प्रिंटआउट्स पर चर्चा करते हैं जो क्लास के init मेथड्स से आए:
प्रत्येक __init__ मेथड एक बार और केवल एक बार कॉल हुआ।
TextDescriber क्लास ने 2 क्लासेस से इनहेरिट किया है जो Tokenizer से इनहेरिट करती हैं। फिर Tokenizer.__init__ दो बार क्यों कॉल नहीं हुआ?
यदि हम अपने सभी super कॉल्स को पुराने तरीके से बदल दें, तो हमें Tokenizer.__init__ के 2 कॉल मिलेंगे। super के कॉल्स हमारे पैटर्न के बारे में थोड़ा और 'सोचते' हैं और A की अतिरिक्त यात्रा को छोड़ देते हैं।
प्रत्येक __init__ मेथड, दूसरों के खत्म होने से पहले शुरू हुआ।
यदि आप ऐसी attribute सेट करने की कोशिश कर रहे हैं जिसका नाम किसी अन्य parent क्लास से टकराता है, तो प्रत्येक __init__ के शुरू और खत्म होने के क्रम पर ध्यान देना महत्वपूर्ण है। वह attribute ओवरराइट हो जाएगी, और यह बहुत भ्रमित कर सकता है।
हमारे मामले में, हमने inherited attributes के साथ नामकरण टकरावों से परहेज़ किया, इसलिए सब कुछ अपेक्षा के अनुसार काम कर रहा है।
फिर से कहें तो, diamond problem तेज़ी से जटिल हो सकता है और अप्रत्याशित परिणाम दे सकता है। प्रोग्रामिंग के अधिकांश मामलों में, जटिल डिज़ाइनों से बचना बेहतर होता है।
हमने क्या सीखा
- हमने
superफ़ंक्शन के बारे में सीखा और यह कैसे single inheritance मेंParentName.methodको बदल सकता है। यह एक अधिक maintainable अभ्यास हो सकता है। - हमने multiple inheritance के बारे में सीखा और कैसे हम कई parent क्लासेस की कार्यक्षमता को एक single child क्लास में आगे बढ़ा सकते हैं।
- हमने multiple-resolution order के बारे में सीखा और यह कैसे तय करता है कि multiple inheritance में parent मेथड्स के बीच नाम टकराव होने पर क्या होता है।
- हमने diamond problem के बारे में सीखा और देखा कि
superके उपयोग से diamond को कैसे नेविगेट किया जाता है।
Inheritance का त्वरित अवलोकन
जैसे-जैसे आप अपने Python प्रोजेक्ट्स और पैकेज बढ़ाते हैं, आप स्वाभाविक रूप से क्लासेस का उपयोग करना चाहेंगे और ऐसा करते समय DRY (don't-repeat-yourself) सिद्धांत लागू करना चाहेंगे। क्लास inheritance, DRY बने रहने के लिए किसी अन्य क्लास के आधार पर एक क्लास बनाने का शानदार तरीका है। यह पोस्ट inheritance की उन्नत अवधारणाओं को कवर करेगी, और बुनियादी inheritance पर विस्तार से चर्चा नहीं की जाएगी। हम एक त्वरित परिचय देंगे, लेकिन इसके लिए बहुत बेहतर, विस्तृत परिचय उपलब्ध हैं। शुरुआत करने के लिए यहाँ कुछ संसाधन हैं: Object-Oriented Programming in Python कोर्स और Python Object-Oriented Programming (OOP): Tutorial।
तो क्लास inheritance क्या है? जनेटिक्स की तरह, एक child क्लास parent से attributes और methods 'इनहेरिट' कर सकती है। आइए उदाहरण के लिए कुछ कोड में सीधे कूदते हैं। नीचे दिए गए कोड ब्लॉक में हम
Childक्लास कोParentक्लास से inherit करते हुए दिखाएँगे।Input
Output
हम देखते हैं कि
Childक्लास नेParentक्लास से attributes और methods 'इनहेरिट' किए। हमारी ओर से किसी अतिरिक्त काम के बिना,Parent.parent_methodअबChildक्लास का हिस्सा है।Parent.__init__()मेथड के लाभ पाने के लिए हमें इस मेथड को स्पष्ट रूप से कॉल करना पड़ा औरselfपास करना पड़ा। ऐसा इसलिए है क्योंकि जब हमनेChildमें__init__मेथड जोड़ा, तो हमने inherited__init__को ओवरराइट कर दिया।इस संक्षिप्त, गैर-विस्तृत अवलोकन के बाद अब पोस्ट के मुख्य भाग पर चलते हैं।
superका परिचयसबसे सरल स्थिति में,
superफ़ंक्शन का उपयोगParent.__init__(self)को सीधे कॉल करने के स्थान पर किया जा सकता है। पहले सेक्शन का हमारा परिचयात्मक उदाहरण नीचे दिखाए अनुसारsuperके साथ फिर से लिखा जा सकता है। ध्यान दें कि नीचे दिया गया कोड ब्लॉक Python 3 में लिखा गया है, पहले के वर्ज़न थोड़े अलग सिंटैक्स का उपयोग करते हैं। इसके अतिरिक्त, आउटपुट हटा दिया गया है क्योंकि वह पहले कोड ब्लॉक के समान है।ईमानदारी से कहें तो, इस मामले में
superहमें बहुत कम, अगर कोई, लाभ देता है। हमारे parent क्लास के नाम पर निर्भर करते हुए हम कुछ कीस्ट्रोक्स बचा सकते हैं, और हमें__init__कॉल मेंselfपास नहीं करना पड़ता। नीचे single inheritance मामलों मेंsuperके उपयोग के कुछ लाभ और हानियाँ दी गई हैं।कमियाँ
यह दलील दी जा सकती है कि यहाँ
superका उपयोग कोड को कम explicit बनाता है। कोड को कम explicit बनाना The Zen of Python का उल्लंघन करता है, जो कहता है, "Explicit is better than implicit."लाभ
सिंगल इनहेरिटेंस में भी
superके पक्ष में एक maintainability तर्क दिया जा सकता है। यदि किसी भी कारण से आपकी child क्लास का inheritance पैटर्न बदलता है (जैसे parent क्लास बदल जाए या multiple inheritance की ओर शिफ्ट हो जाए) तो आपकोParentClass.method_name()के सभी बचे हुए संदर्भों को खोजकर बदलने की ज़रूरत नहीं होगी;superका उपयोग करने सेclassस्टेटमेंट में हुए बदलाव के साथ सारी परिवर्तन स्वतः प्रवाहित हो जाएँगे।