الوراثة المتعددة (Multiple Inheritance) في Python

هل يمكن للكائن أن يمتلك أكثر من أب؟ في عالم بايثون، الإجابة هي نعم! الوراثة المتعددة (Multiple Inheritance) هي ميزة قوية تتيح لك تعلم البرمجة بالعربي بأسلوب متقدم، حيث يمكن لصنف واحد أن يجمع بين قدرات وميزات عدة أصناف مختلفة في وقت واحد.

1. تعريف الوراثة المتعددة (What is Multiple Inheritance?)

الوراثة المتعددة هي قدرة الصنف (Class) على الوراثة من أكثر من صنف أب (Parent Class). هذا يعني أن الصنف الابن سيحتوي على جميع الخصائص والأساليب المعرفة في جميع الأصناف الآباء التي ورث منها.

بينما تمنع لغات برمجة أخرى (مثل Java) الوراثة المتعددة لتجنب التعقيد، فإن بايثون تدعمها بشكل كامل وتوفر أدوات ذكية لإدارتها، مما يجعلها من أكثر دروس برمجة عربية إثارة للمبرمجين الذين يبحثون عن المرونة القصوى.

مثال من حياتنا

تخيل "الهاتف الذكي". هو في الحقيقة يرث ميزات من عدة أجهزة:

  • يرث من "الهاتف التقليدي" (القدرة على إجراء المكالمات).
  • يرث من "الكاميرا" (القدرة على التصوير).
  • يرث من "الحاسوب" (القدرة على تصفح الإنترنت).

الهاتف الذكي هنا هو "ابن" لثلاثة "آباء" مختلفين، ويجمع كل ميزاتهم في جهاز واحد.

2. لماذا نستخدم الوراثة المتعددة؟ (Why Use It?)

يساعدنا شرح البرمجة باللغة العربية على فهم أن الوراثة المتعددة ليست مجرد تعقيد إضافي، بل هي حل لمشاكل برمجية معينة:

  • دمج الوظائف (Functionality Mixing): عندما تريد إنشاء صنف يجمع بين سلوكيات مستقلة تماماً.
  • تجنب التكرار: بدلاً من إعادة كتابة كود الكاميرا داخل الهاتف، نرثه من صنف الكاميرا الجاهز.
  • تصميم الأنظمة المعقدة: تتيح بناء أنظمة مرنة تشبه تركيب قطع الليغو، حيث تختار الميزات التي تريدها من أصناف مختلفة.

3. الصيغة العامة (General Syntax)

لتحقيق الوراثة المتعددة، نقوم بكتابة أسماء الأصناف الآباء داخل القوسين، مفصولين بفاصلة ,.

multiple_syntax.py
class Parent1:
    def feature1(self):
        print("ميزة من الأب الأول")

class Parent2:
    def feature2(self):
        print("ميزة من الأب الثاني")

# الابن يرث من الاثنين معاً
class Child(Parent1, Parent2):
    pass

obj = Child()
obj.feature1()
obj.feature2()
شرح الكود سطراً بسطر:
  1. class Parent1: - نعرّف الصنف الأب الأول.
  2. def feature1(self): - دالة خاصة بالأب الأول.
  3. class Parent2: - نعرّف الصنف الأب الثاني.
  4. def feature2(self): - دالة خاصة بالأب الثاني.
  5. class Child(Parent1, Parent2): - مهم! الابن يرث من أبوين معاً (لاحظ الفاصلة بينهما).
  6. pass - الابن لا يضيف شيئاً جديداً، فقط يرث.
  7. obj = Child() - ننشئ كائناً من الابن.
  8. obj.feature1() - نستدعي دالة موروثة من الأب الأول.
  9. obj.feature2() - نستدعي دالة موروثة من الأب الثاني.

النتيجة: الكائن الواحد يمتلك ميزات من عدة أصناف!

4. أمثلة تطبيقية (Practical Examples)

أ- مثال بسيط: دمج القدرات

لنطبق مثال الهاتف الذكي الذي ذكرناه سابقاً برمجياً.

smartphone_inheritance.py
class Camera:
    def take_photo(self):
        print("تم التقاط صورة احترافية!")

class MusicPlayer:
    def play_music(self):
        print("جاري تشغيل الموسيقى...")

class SmartPhone(Camera, MusicPlayer):
    def make_call(self):
        print("جاري الاتصال...")

# إنشاء هاتف ذكي
my_phone = SmartPhone()

my_phone.take_photo()  # موروثة من Camera
my_phone.play_music()  # موروثة من MusicPlayer
my_phone.make_call()   # خاصة بـ SmartPhone
شرح الكود سطراً بسطر:
  1. class Camera: - صنف الكاميرا (الأب الأول).
  2. def take_photo(self): - دالة لالتقاط الصور.
  3. class MusicPlayer: - صنف مشغل الموسيقى (الأب الثاني).
  4. def play_music(self): - دالة لتشغيل الموسيقى.
  5. class SmartPhone(Camera, MusicPlayer): - الهاتف الذكي يرث من الكاميرا ومشغل الموسيقى معاً!
  6. def make_call(self): - دالة جديدة خاصة بالهاتف فقط.
  7. my_phone = SmartPhone() - ننشئ هاتفاً ذكياً.
  8. my_phone.take_photo() - نستخدم ميزة موروثة من Camera.
  9. my_phone.play_music() - نستخدم ميزة موروثة من MusicPlayer.
  10. my_phone.make_call() - نستخدم ميزة خاصة بالهاتف.

الفائدة: الهاتف الذكي يجمع 3 وظائف مختلفة في جهاز واحد!

تعلم الكود بالعربية يصبح أسهل عندما ترى كيف تجتمع الوظائف المختلفة بسلاسة في كائن واحد.

ب- مثال واقعي: نظام الصلاحيات

تخيل نظاماً للمستخدمين حيث يمتلك المدير صلاحيات القراءة والكتابة معاً.

class Reader:
    def read_file(self):
        print("قراءة البيانات من الملف...")

class Writer:
    def write_file(self):
        print("كتابة البيانات في الملف...")

class Admin(Reader, Writer):
    def delete_all(self):
        print("حذف جميع البيانات!")

admin_user = Admin()
admin_user.read_file()
admin_user.write_file()
admin_user.delete_all()
شرح الكود سطراً بسطر:
  1. class Reader: - صنف يمنح صلاحية القراءة.
  2. def read_file(self): - دالة لقراءة الملفات.
  3. class Writer: - صنف يمنح صلاحية الكتابة.
  4. def write_file(self): - دالة لكتابة الملفات.
  5. class Admin(Reader, Writer): - المدير يرث من الاثنين ليحصل على صلاحيات القراءة والكتابة.
  6. def delete_all(self): - صلاحية إضافية خاصة بالمدير فقط.
  7. admin_user = Admin() - ننشئ مستخدم مدير.
  8. admin_user.read_file() - المدير يستطيع القراءة (موروثة).
  9. admin_user.write_file() - المدير يستطيع الكتابة (موروثة).
  10. admin_user.delete_all() - المدير يستطيع الحذف (خاصة به).

الفكرة: نجمع الصلاحيات من أصناف مختلفة بدلاً من تكرار الكود.

5. مشكلة الماسة (The Diamond Problem) و MRO

سؤال ذكي: ماذا لو كان الأبوان يمتلكان دالة بنفس الاسم؟ أي واحدة سيختار الابن؟

تسمى هذه المعضلة بـ "مشكلة الماسة". في بايثون، يتم حلها باستخدام نظام يسمى MRO (Method Resolution Order)، وهو ترتيب البحث عن الدوال.

mro_example.py
class A:
    def show(self):
        print("أنا من الصنف A")

class B(A):
    def show(self):
        print("أنا من الصنف B")

class C(A):
    def show(self):
        print("أنا من الصنف C")

class D(B, C):
    pass

obj = D()
obj.show()

# لرؤية ترتيب البحث
print(D.mro())
النتيجة
أنا من الصنف B [, , , , ]
شرح الكود سطراً بسطر:
  1. class A: - الصنف الجد الأعلى في الهرم.
  2. def show(self): - دالة في الجد A.
  3. class B(A): - B يرث من A.
  4. def show(self): - B يعيد تعريف show (Overriding).
  5. class C(A): - C يرث من A أيضاً.
  6. def show(self): - C يعيد تعريف show أيضاً.
  7. class D(B, C): - مشكلة الماسة! D يرث من B و C، وكلاهما يرث من A.
  8. pass - D لا يعرّف show بنفسه.
  9. obj = D() - ننشئ كائناً من D.
  10. obj.show() - أي show سيتم استدعاؤها؟ B أم C؟
  11. print(D.mro()) - نطبع ترتيب البحث (Method Resolution Order).

النتيجة: بايثون تبحث بالترتيب: D → B → C → A → object

لذلك: تم استدعاء show من B لأنها الأولى في الترتيب (من اليسار إلى اليمين).

القاعدة الذهبية: بايثون تبحث من اليسار إلى اليمين. بما أننا كتبنا D(B, C)، فإنها ستبحث في B أولاً، وإذا وجدت الدالة ستتوقف.

6. أخطاء شائعة (Common Errors)

1. تضارب الأسماء (Naming Conflicts)

استخدام نفس اسم المتغير أو الدالة في عدة آباء قد يؤدي لنتائج غير متوقعة إذا لم تكن فاهماً لنظام MRO.

2. التعقيد الزائد

الوراثة من 5 أو 6 أصناف تجعل الكود "كابوساً" في الصيانة. إذا احتجت لهذا العدد، فربما تصميمك يحتاج لإعادة نظر.

7. تمرين عملي (Exercise)

تحدي الكائن "البرمائي"

قم بتصميم نظام يحاكي مركبة برمائية (تتحرك في البر والماء):

  1. أنشئ صنف LandVehicle يحتوي على دالة drive.
  2. أنشئ صنف WaterVehicle يحتوي على دالة swim.
  3. أنشئ صنف AmphibiousVehicle يرث من الاثنين.
  4. أضف دالة في الابن تسمى move تستدعي كلاً من drive و swim.

هذا التمرين سيثبت فهمك لكيفية دمج القدرات البرمجية.

الخطوة التالية

بعد أن تعلمنا كيف نجمع الميزات، حان الوقت لنتعلم كيف "نحميها". في الدرس القادم، سنتعرف على التغليف (Encapsulation) وكيفية إخفاء البيانات الحساسة داخل الكائنات.

الدرس التالي: التغليف (Encapsulation) في Python
المحرر الذكي

اكتب الكود وشاهد النتيجة فوراً

جرب الآن مجاناً
قناة ديف عربي

تابع أحدث الدروس والتحديثات مباشرة على واتساب

انضم الآن