Python में multiple inheritance एक ऐसी सुविधा है, जहां किसी क्लास को एक साथ कई पैरेंट क्लासों से attributes और methods विरासत में मिल सकते हैं। इसकी मदद से आप बहुत से क्लासों में मौजूद व्यवहार और गुणों को मिलाकर एक नई क्लास बना सकते हैं, जो काफी शक्तिशाली हो सकती है। लेकिन जहां ताक़त होती है, वहां थोड़ी जटिलता भी होती है, इसलिए multiple inheritance को सावधानी से इस्तेमाल करना चाहिए।
multiple inheritance को समझने के लिए, आइए एक multimedia file system का उदाहरण देखें। मान लीजिए हमारे पास एक Video क्लास और एक Audio क्लास है। अब हम ऐसी क्लास बनाना चाहते हैं, जिसमें वीडियो और ऑडियो दोनों हों। इसे हम multiple inheritance का इस्तेमाल करके हासिल कर सकते हैं:
class Video: # 'Video' नाम की क्लास परिभाषित करता है
def __init__(self):
self.video_codec = 'H.264'
def play_video(self):
return 'Playing video...'
class Audio: # 'Audio' नाम की क्लास परिभाषित करता है
def __init__(self):
self.audio_codec = 'AAC'
def play_audio(self):
return 'Playing audio...'
class Multimedia(Video, Audio): # 'Video' और 'Audio' से विरासत में लेने वाली 'Multimedia' सबक्लास परिभाषित करता है
def __init__(self):
Video.__init__(self) # पैरेंट 'Video' क्लास के '__init__' मेथड को कॉल करता है
Audio.__init__(self) # पैरेंट 'Audio' क्लास के '__init__' मेथड को कॉल करता है
def play(self):
return self.play_video() + ' ' + self.play_audio() # दोनों video और audio चलाता है
multimedia = Multimedia() # 'Multimedia' क्लास का एक instance बनाता है
print(multimedia.play()) # आउटपुट: 'Playing video... Playing audio...'
इस उदाहरण में, Multimedia क्लास Video और Audio दोनों से विरासत लेती है। हम Multimedia के कॉन्स्ट्रक्टर में Video.__init__() और Audio.__init__() को कॉल करके सभी attributes को सही तरीके से इनिशियलाइज़ करते हैं। play() मेथड दोनों पैरेंट क्लासों की मेथड्स—play_video() और play_audio()—को एक साथ चलाकर वीडियो और ऑडियो दोनों को प्ले करता है।
Multiple inheritance हमें कई क्लासों के व्यवहारों को मिलाने की सुविधा देता है, लेकिन इसका इस्तेमाल सावधानी से करना चाहिए, क्योंकि इससे जटिलताएं बढ़ सकती हैं।
🤔
अगर पैरेंट क्लासों में एक जैसे नाम की मेथड्स हों, तो इनहेरिटेंस का क्रम (order) बहुत मायने रखता है, क्योंकि Python सबक्लास डिफिनिशन में लिखे गए पैरेंट्स के क्रम में मेथड ढूंढ़ता है।
Method Resolution Order
Python uses an algorithm called C3 linearization, or just C3, to determine the method resolution order when classes inherit from multiple parents. The idea is to preserve the order in which classes are defined in the subclass declaration, while also maintaining the order from the parent classes. However, without getting into the complexities of the algorithm, you can easily check the MRO for a class using the mro() method:
class A: # Define a class named 'A'
def process(self):
return 'A process'
class B(A): # Define a class named 'B' that inherits from 'A'
def process(self):
return 'B process'
class C(A): # Define a class named 'C' that inherits from 'A'
def process(self):
return 'C process'
class D(B, C): # Define a class named 'D' that inherits from 'B' and 'C'
pass
print(D.mro()) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
In the example above, the MRO for class D is D -> B -> C -> A -> object. This means that when you call a method on an instance of D, Python first looks at the method in D. If it doesn't find it there, it moves on to B, then C, and then A.
If you want to call a specific parent class's method, you can directly call it using the parent class's name. However, this is generally not recommended because it can lead to code that is hard to maintain.
Let's modify class D to call the process method of class A:
class D(B, C): # Define a class named 'D' that inherits from 'B' and 'C'
def process(self):
return A.process(self)
d = D() # Create an instance of the 'D' class
print(d.process()) # A process
In the example above, even though class D inherits from B and C (both of which have a process method), we specifically call the process method from class A. This ensures that no matter what the MRO is, class A's process method is used.
Notice how the self is passed explicitly as an argument. In Python, self is a reference to the current instance of the class and is used to access variables and methods associated with that instance. It's implicitly passed as the first argument to instance methods. When we're calling A.process(self), we're saying "Call the process method of class A on this specific instance (represented by self)". Even though the method is defined in A, it's being executed in the context of the instance of class D. This means it has access to any attributes or methods defined in class D or its parent classes. In short, passing self to A.process allows us to invoke the process method from A on the instance of D.
Python में, जब क्लास कई पैरेंट क्लासों से विरासत लेती है, तब मेथड रेज़ॉल्यूशन ऑर्डर (MRO) तय करने के लिए एक एल्गोरिथ्म इस्तेमाल होता है जिसे C3 linearization कहते हैं। यह एल्गोरिथ्म सबक्लास में पैरेंट क्लासों के लिखे गए क्रम को बनाए रखता है, साथ ही पैरेंट क्लासों के अपने ऑर्डर का भी ध्यान रखता है। इस एल्गोरिथ्म के विवरण में ज्यादा गहराई में गए बिना, आप किसी भी क्लास का MRO देखने के लिए mro() मेथड का इस्तेमाल कर सकते हैं:
Challenge: The Frog Class
प्रोग्रामिंग की एक जादुई दुनिया में कई तरह के जानवरों से जुड़ी क्लासें हैं: Fish, WaterAnimal, और LandAnimal।
Fish, जो WaterAnimal से विरासत लेती है और इसमें swim() मेथड होती है।
WaterAnimal में live_in_water() मेथड होती है।
LandAnimal में live_on_land() मेथड होती है।
अब आपको एक Frog क्लास बनानी है, जो WaterAnimal और LandAnimal दोनों से इनहेरिट करती हो। इस क्लास में पैरेंट क्लासों की सभी मेथड्स को कॉल कर पाने की क्षमता होनी चाहिए।
हर क्लास में एक species नाम का attribute होना चाहिए, जो ऑब्जेक्ट बनाते समय पास किया जाए।
जब भी कोई मेथड कॉल की जाए, उसे अपने species के नाम सहित एक उपयुक्त संदेश प्रिंट करना होगा। (जैसा नीचे के उदाहरण में दिखाया गया है)