أساسيات بايثون 36: شرح الوراثة Inheritance في Python للمبتدئين

شرح الوراثة Inheritance في Python للمبتدئين باستخدام Parent Class وChild Class

بعد أن تعلمت في الدروس السابقة معنى class وobject والفرق بين خصائص الكلاس وخصائص الكائن، ستلاحظ أن بعض الكلاسات تتشابه كثيرًا. مثلًا: الطالب والمعلم كلاهما يملك اسمًا وبريدًا إلكترونيًا وطريقة لعرض معلوماته، لكن لكل واحد منهما بيانات وسلوك إضافي مختلف.

بدل أن تكرر نفس الكود في أكثر من class، يمكنك استخدام الوراثة Inheritance. الوراثة تسمح لك بإنشاء class جديد يرث الخصائص والدوال الأساسية من class موجود، ثم تضيف إليه ما يحتاجه أو تعدّل بعض سلوكه بطريقة منظمة.

في هذا الدرس ستتعلم معنى Parent Class وChild Class، وكيف تستخدم super()، وكيف تعدّل method موروثة، ومتى تكون الوراثة اختيارًا جيدًا ومتى لا تكون كذلك.

{alertInfo} الفكرة ببساطة: إذا كان Student يعتبر نوعًا من Person، فمن المنطقي أن يرث الطالب الاسم والبريد ودالة التعريف من class الشخص، ثم يضيف بياناته الخاصة مثل الدرجة أو التخصص.

{getToc} $title={محتوى المقال}

ما هي الوراثة Inheritance في Python؟

الوراثة هي آلية في البرمجة كائنية التوجه تسمح لـ class جديد بأن يستفيد من خصائص ودوال class موجود مسبقًا. نسمي class الأصلي عادة:

  • Parent Class أو Base Class أو الكلاس الأب.

ونسمي class الذي يرث منه:

  • Child Class أو Derived Class أو الكلاس الابن.

توضح وثائق Python أن الكلاس المشتق يستطيع أن يرث من كلاس أساسي، وأنه يمكنه إعادة تعريف methods موروثة أو استدعاء method موجودة في الكلاس الأب عند الحاجة.

لماذا نستخدم الوراثة؟

الفائدة الرئيسية من الوراثة هي إعادة استخدام الكود. بدل نسخ نفس الخصائص والدوال داخل كل class، تكتب الجزء المشترك مرة واحدة داخل الكلاس الأب.

بدون وراثة باستخدام الوراثة
تكرر name وemail ودالة عرض البيانات في كل class. تضع البيانات المشتركة في Person وتورّثها إلى Student وTeacher.
تعديل الدالة المشتركة يحتاج تعديلها في أماكن متعددة. تعدلها في class الأب، فتستفيد منها الكلاسات الأبناء.
يزداد احتمال وجود اختلافات وأخطاء بين الكلاسات المتشابهة. الكود يصبح أوضح عندما توجد علاقة حقيقية من نوع “هو نوع من”.

لكن لا تستخدم الوراثة فقط لأن كلاسَين فيهما بعض التشابه. اسأل: هل الكلاس الابن فعلًا نوع خاص من الكلاس الأب؟ الطالب هو شخص، والسيارة ليست شخصًا. إذا كانت العلاقة ليست “هو نوع من”، فقد يكون التركيب أو تمرير كائن داخل كائن خيارًا أوضح لاحقًا.

أول مثال: Person وStudent

سننشئ أولًا class باسم Person فيها الاسم والبريد الإلكتروني ودالة تعريف بسيطة:

class Person:
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def introduce(self):
        print(f"مرحبًا، أنا {self.name}.")

الآن ننشئ class باسم Student ترث من Person. نكتب اسم الأب داخل الأقواس بعد اسم class:

class Student(Person):
    pass

حتى بدون كتابة أي دوال داخل Student، فإنها ترث ما هو موجود في Person. جرّب:

student = Student("علي", "ali@example.com")

print(student.name)
print(student.email)
student.introduce()

الناتج:

علي
ali@example.com
مرحبًا، أنا علي.

لم نكتب __init__ أو introduce() داخل Student، لكن الطالب استطاع استخدامها لأنها موروثة من Person.

الفرق بين Parent Class وChild Class في Python مع مثال Person وStudent

إضافة خصائص جديدة إلى Child Class

الطالب لا يملك فقط الاسم والبريد؛ قد يملك تخصصًا ودرجة. لذلك نضيف خصائص خاصة به داخل class الابن.

هنا نحتاج إلى دالة __init__ داخل Student. لكن إذا كتبناها، يجب أن نهيئ بيانات الأب أيضًا. لهذا نستخدم super().

class Person:
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def introduce(self):
        print(f"مرحبًا، أنا {self.name}.")

class Student(Person):
    def __init__(self, name, email, major, score):
        super().__init__(name, email)

        self.major = major
        self.score = score

student = Student(
    "سارة",
    "sara@example.com",
    "علوم الحاسوب",
    92
)

print(student.name)
print(student.major)
print(student.score)

ما هي super() ولماذا نستخدمها؟

الدالة super() تمنحك طريقة للوصول إلى الكلاس الأب من داخل الكلاس الابن. في هذا المثال:

super().__init__(name, email)

تعني: “نفّذ دالة __init__ الموجودة في Person، وأرسل إليها الاسم والبريد الإلكتروني”. وبذلك لا تحتاج إلى كتابة السطرين التاليين مجددًا داخل class الابن:

self.name = name
self.email = email

بعد استدعاء الأب، تكمل class الابن بخصائصها الإضافية مثل major وscore.

{alertWarning} خطأ شائع: عندما تكتب __init__ جديدة داخل class الابن، لا تُستدعى __init__ الخاصة بالأب تلقائيًا. استخدم super().__init__(...) إذا كنت تحتاج تهيئة خصائص الكلاس الأب.
استخدام super و init داخل الوراثة في Python

استخدام methods موروثة من Parent Class

الطالب ما زال قادرًا على استخدام introduce() الموروثة من Person:

student.introduce()

وسيطبع:

مرحبًا، أنا سارة.

ويمكننا إضافة method جديدة للطالب فقط، مثل التحقق من النجاح:

class Student(Person):
    def __init__(self, name, email, major, score):
        super().__init__(name, email)

        self.major = major
        self.score = score

    def is_passed(self):
        return self.score >= 60

هنا introduce() جاءت من الأب، بينما is_passed() تخص الطالب فقط.

تعديل Method موروثة: Method Overriding

أحيانًا تكون method الأب مناسبة كبداية، لكن الكلاس الابن يحتاج سلوكًا أكثر تحديدًا. يمكنك كتابة method بالاسم نفسه داخل class الابن؛ وهذا يسمى Method Overriding.

في المثال التالي، الكلاس الأب يعرّف الشخص بشكل عام، بينما الطالب يضيف التخصص إلى رسالة التعريف:

class Person:
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def introduce(self):
        print(f"أنا {self.name}.")

class Student(Person):
    def __init__(self, name, email, major):
        super().__init__(name, email)
        self.major = major

    def introduce(self):
        print(
            f"أنا {self.name}، طالب في تخصص {self.major}."
        )

عند تشغيل:

student = Student(
    "علي",
    "ali@example.com",
    "الذكاء الاصطناعي"
)

student.introduce()

سيستخدم Python دالة introduce() الموجودة في Student بدل الدالة الموجودة في Person.

كيف تضيف سلوك الابن إلى سلوك الأب؟

بدل نسخ رسالة الأب بالكامل، يمكنك استدعاء method الأب عبر super() ثم تضيف سطرًا جديدًا:

class Student(Person):
    def __init__(self, name, email, major):
        super().__init__(name, email)
        self.major = major

    def introduce(self):
        super().introduce()
        print(f"تخصصي هو: {self.major}")

بهذه الطريقة تستفيد من سلوك الأب وتضيف إليه سلوكًا خاصًا بالابن.

تعديل method موروثة في Child Class باستخدام method overriding في Python

مثال عملي كامل: Person وStudent وTeacher

المثال التالي يوضح كيف يمكن لأكثر من class أن يرث من الأب نفسه:

class Person:
    academy_name = "أكاديمية بايثون العرب"

    def __init__(self, name, email):
        self.name = name
        self.email = email

    def show_contact(self):
        print(f"الاسم: {self.name}")
        print(f"البريد: {self.email}")

class Student(Person):
    def __init__(self, name, email, major, score):
        super().__init__(name, email)
        self.major = major
        self.score = score

    def show_role(self):
        status = "ناجح" if self.score >= 60 else "يحتاج مراجعة"
        print(f"طالب في: {self.major}")
        print(f"الحالة: {status}")

class Teacher(Person):
    def __init__(self, name, email, subject):
        super().__init__(name, email)
        self.subject = subject

    def show_role(self):
        print(f"مدرس مادة: {self.subject}")

student = Student(
    "سارة",
    "sara@example.com",
    "علوم الحاسوب",
    88
)

teacher = Teacher(
    "خالد",
    "khaled@example.com",
    "Python"
)

print(Person.academy_name)
student.show_contact()
student.show_role()

print("-" * 30)

teacher.show_contact()
teacher.show_role()

في هذا المثال:

  • Person يحتوي على البيانات والسلوك المشترك.
  • Student يرث الاسم والبريد ويضيف التخصص والدرجة.
  • Teacher يرث الاسم والبريد ويضيف المادة التي يدرسها.
  • الكل يستطيع الوصول إلى academy_name لأنها class attribute موروثة من الأب.

كيف تعرف هل كائن يرث من class معين؟

توفر Python دالة isinstance() للتحقق هل كائن معين ينتمي إلى class أو إلى class يرث منه.

print(isinstance(student, Student))
print(isinstance(student, Person))

الناتج سيكون:

True
True

السبب أن student هو كائن من Student، وStudent يرث من Person.

وتستطيع أيضًا التحقق من العلاقة بين classين باستخدام issubclass():

print(issubclass(Student, Person))

أخطاء شائعة عند استخدام الوراثة

1. نسيان كتابة Parent Class بين الأقواس

هذا الكود لا يجعل Student يرث من Person:

class Student:
    pass

الصحيح:

class Student(Person):
    pass

2. نسيان super().__init__ داخل Child Class

إذا أنشأت __init__ داخل الابن ثم استخدمت خصائص الأب لاحقًا، قد يظهر خطأ مثل:

AttributeError: 'Student' object has no attribute 'name'

لأنك لم تنفذ تهيئة الأب التي تنشئ self.name وself.email.

3. تكرار كود الأب بدل استدعائه

يمكنك كتابة خصائص الأب يدويًا داخل الابن، لكن هذا يجعل الصيانة أصعب. الأفضل غالبًا استخدام super().__init__() ما دام الابن يحتاج نفس التهيئة.

4. استخدام الوراثة لعلاقة غير منطقية

لا تجعل Car ترث من Person لمجرد أن كليهما يملك اسمًا. الوراثة تعبر عن علاقة “هو نوع من”، مثل Student هو نوع من Person.

5. تعديل method دون استدعاء الأب عند الحاجة

عندما تعيد تعريف method، تختفي نسخة الأب من الاستخدام المباشر على الكائن. إذا أردت الإبقاء على جزء من سلوك الأب، استدعها عبر super().method_name().

أخطاء شائعة عند استخدام الوراثة Inheritance في Python

متى لا تحتاج إلى الوراثة؟

الوراثة مفيدة، لكنها ليست الحل لكل شيء. لا تستخدمها فقط لتقليل عدد الأسطر. إذا كانت العلاقة أقرب إلى “يحتوي على” بدل “هو نوع من”، فكر في وضع كائن داخل كائن بدل الوراثة.

مثلًا: الطالب ليس نوعًا من Course، لكنه يمكن أن يسجل في دورة. هنا يمكن أن تحتوي class الطالب على قائمة دورات أو كائنات دورات، ولا نحتاج أن يرث من Course.

تمرين عملي

أنشئ class أب باسم Vehicle يحتوي على:

  • خصائص: brand وyear.
  • دالة باسم show_info() تعرض معلومات المركبة.

ثم أنشئ class ابن باسم Car يرث من Vehicle ويضيف:

  • خاصية باسم doors.
  • تعديل show_info() حتى تعرض عدد الأبواب أيضًا.

حل مقترح

class Vehicle:
    def __init__(self, brand, year):
        self.brand = brand
        self.year = year

    def show_info(self):
        print(f"الماركة: {self.brand}")
        print(f"السنة: {self.year}")

class Car(Vehicle):
    def __init__(self, brand, year, doors):
        super().__init__(brand, year)
        self.doors = doors

    def show_info(self):
        super().show_info()
        print(f"عدد الأبواب: {self.doors}")

car = Car("Toyota", 2024, 4)
car.show_info()

روابط داخلية مفيدة من بايثون العرب

الخلاصة

  • الوراثة تسمح للكلاس الابن أن يرث خصائص ودوال الكلاس الأب.
  • نكتب class الابن بهذا الشكل: class Student(Person):.
  • استخدم super().__init__(...) لتهيئة خصائص الأب من داخل الابن.
  • يمكن للابن إضافة خصائص ودوال خاصة به دون تعديل الكلاس الأب.
  • يمكنك تعديل method موروثة عبر Method Overriding، مع استدعاء نسخة الأب عبر super().method_name() عند الحاجة.
  • استخدم الوراثة عندما تكون العلاقة منطقية: الابن “هو نوع من” الأب.
{alertSuccess} خطوتك التالية: عدّل مثال Person وأنشئ class جديدة باسم Admin ترث منه، ثم أضف لها خاصية permissions ودالة تعرض صلاحيات المسؤول. هكذا ستتأكد أنك فهمت الفرق بين البيانات المشتركة والبيانات الخاصة بكل نوع.

مصادر خارجية رسمية للتوسع

أسئلة شائعة

ما هي الوراثة Inheritance في Python؟

هي طريقة لإنشاء class جديد يرث الخصائص والدوال من class موجود، ثم يضيف أو يغير ما يحتاجه. مثل Student الذي يرث من Person.

ما الفرق بين Parent Class وChild Class؟

Parent Class هو الكلاس الذي يحتوي على السلوك والبيانات الأساسية المشتركة. Child Class هو الكلاس الذي يرث منه ويضيف خصائص أو methods خاصة به.

لماذا نستخدم super في Python؟

نستخدم super() لاستدعاء method من الكلاس الأب، خصوصًا super().__init__() لتهيئة الخصائص المشتركة بدل تكرارها داخل الكلاس الابن.

هل يجب أن يرث كل class من class آخر؟

لا. استخدم الوراثة فقط عندما توجد علاقة منطقية من نوع “هو نوع من”. إذا كانت العلاقة “يحتوي على”، فكر في استخدام composition بدلًا من الوراثة.

هل يمكن للابن تعديل method موجودة في الأب؟

نعم. اكتب method بالاسم نفسه داخل child class. هذا يسمى Method Overriding، ويمكنك استدعاء نسخة الأب عبر super().method_name() إذا أردت الجمع بين السلوكين.

إرسال تعليق

أحدث أقدم