الوراثة (Inheritance) في Python
هل تساءلت يوماً كيف تُبنى الأنظمة الضخمة دون تكرار آلاف الأسطر من الكود؟ السر يكمن في الوراثة (Inheritance)، وهي الركيزة الأساسية التي تجعل تعلم البرمجة من الصفر بالعربية رحلة ممتعة ومنطقية.
1. تعريف الوراثة
الوراثة في البرمجة الكائنية هي آلية تسمح لصنف جديد (يسمى الصنف الابن أو Child Class) باكتساب الخصائص والأساليب من صنف موجود مسبقاً (يسمى الصنف الأب أو Parent Class).
ببساطة، هي عملية نقل "الجينات البرمجية" من الأب إلى الأبناء. فإذا كان الأب يمتلك ميزة معينة، فإن الابن يحصل عليها تلقائياً بمجرد إعلان الوراثة، دون الحاجة لإعادة كتابتها من الصفر.
تشبيه من الواقع
تخيل أنك تصمم لعبة سيارات. بدلاً من تعريف "المحرك" و"العجلات" لكل نوع سيارة (رياضية، شاحنة، دفع رباعي)، يمكنك إنشاء صنف عام يسمى "مركبة" يحتوي على هذه الخصائص المشتركة.
ثم تجعل "السيارة الرياضية" ترث من "المركبة". هكذا تحصل السيارة الرياضية على المحرك والعجلات مجاناً، وتتفرغ أنت لإضافة ميزات خاصة بها مثل "نظام التيربو".
2. لماذا نستخدم الوراثة؟
تعتبر الوراثة من أهم أساسيات البرمجة بالعربية التي يجب على كل مبتدئ إتقانها. إليك الأسباب الجوهرية:
إعادة استخدام الكود (Code Reusability)
بدلاً من كتابة نفس الدوال في عدة أصناف، نكتبها مرة واحدة في الصنف الأب. هذا يوفر الوقت ويقلل من احتمالية الأخطاء.
سهولة الصيانة (Maintenance)
إذا أردت تعديل ميزة مشتركة، ستقوم بتعديلها في مكان واحد فقط (الأب) وسينعكس التعديل على جميع الأبناء تلقائياً.
التنظيم المنطقي (Logical Organization)
تساعد في بناء هيكل هرمي يعكس الواقع، مما يجعل الكود أسهل في الفهم والقراءة. هذا أمر أساسي في أي كورس برمجة كامل بالعربية.
قابلية التوسع (Extensibility)
يمكنك إضافة ميزات جديدة للأصناف الأبناء دون المساس بالكود الأصلي المستقر في الصنف الأب.
3. الصيغة العامة للوراثة
في لغة بايثون، تتم الوراثة عن طريق تمرير اسم الصنف الأب بين قوسين بجانب اسم الصنف الابن عند تعريفه. إليك الصيغة الأساسية:
# تعريف الصنف الأب
class ParentClass:
# خصائص وأساليب الأب
pass
# تعريف الصنف الابن الذي يرث من الأب
class ChildClass(ParentClass):
# خصائص وأساليب إضافية للابن
pass
بمجرد كتابة ChildClass(ParentClass)، أصبح الابن يمتلك كل ما يملكه الأب من خصائص وأساليب.
4. أمثلة تطبيقية
المثال الأول: الوراثة الأساسية
لنبدأ بمثال بسيط جداً يوضح كيف يرث الابن دالة من الأب دون إعادة تعريفها. هذا المثال مثالي لمن يريد تعلم البرمجة للمبتدئين خطوة بخطوة.
class Device:
def power_on(self):
print("الجهاز قيد التشغيل الآن...")
class Phone(Device):
def call(self):
print("جاري إجراء مكالمة...")
# إنشاء كائن من الصنف الابن
my_phone = Phone()
# استدعاء دالة موروثة من الأب
my_phone.power_on()
# استدعاء دالة خاصة بالابن
my_phone.call()
شرح الكود سطراً بسطر:
class Device:- نعرّف صنفاً أب يسمى Device (جهاز).def power_on(self):- نعرّف دالة داخل الصنف الأب لتشغيل الجهاز.print("الجهاز قيد التشغيل...")- طباعة رسالة التشغيل.class Phone(Device):- نعرّف صنفاً ابن يسمى Phone يرث من Device (لاحظ الأب بين القوسين).def call(self):- نعرّف دالة خاصة بالهاتف فقط (غير موجودة في الأب).my_phone = Phone()- ننشئ كائناً من الصنف الابن Phone.my_phone.power_on()- نستدعي دالة موروثة من الأب! الهاتف يستطيع التشغيل لأنه ورث هذه الميزة.my_phone.call()- نستدعي دالة خاصة بالهاتف فقط.
المثال الثاني: نظام إدارة الموظفين (مثال واقعي)
في الأنظمة الحقيقية، نستخدم الوراثة لتنظيم أنواع مختلفة من البيانات التي تشترك في صفات معينة. هذا مثال عملي من عالم الشركات.
class Employee:
def __init__(self, name, id_number):
self.name = name
self.id_number = id_number
def show_basic_info(self):
print(f"الموظف: {self.name} | الرقم الوظيفي: {self.id_number}")
class Developer(Employee):
def __init__(self, name, id_number, language):
# استدعاء باني الأب لتعريف الاسم والرقم
super().__init__(name, id_number)
self.language = language
def show_skills(self):
print(f"{self.name} يبرمج بلغة: {self.language}")
# إنشاء مطور
dev1 = Developer("أحمد", "DEV-101", "Python")
dev1.show_basic_info() # موروثة من الأب
dev1.show_skills() # خاصة بالابن
شرح الكود سطراً بسطر:
class Employee:- نعرّف صنف الموظف الأساسي (الأب).def __init__(self, name, id_number):- باني الصنف الأب يستقبل الاسم والرقم الوظيفي.self.name = name- نحفظ الاسم كخاصية للموظف.self.id_number = id_number- نحفظ الرقم الوظيفي كخاصية.def show_basic_info(self):- دالة لعرض المعلومات الأساسية.class Developer(Employee):- نعرّف صنف المطور الذي يرث من Employee.def __init__(self, name, id_number, language):- باني المطور يستقبل 3 معاملات.super().__init__(name, id_number)- مهم جداً! نستدعي باني الأب لتعريف الاسم والرقم.self.language = language- نضيف خاصية جديدة خاصة بالمطور فقط.dev1 = Developer("أحمد", "DEV-101", "Python")- ننشئ كائن مطور بثلاث معلومات.dev1.show_basic_info()- نستدعي دالة موروثة من الأب.dev1.show_skills()- نستدعي دالة خاصة بالمطور.
المثال الثالث: تعديل الأساليب (Method Overriding)
أحياناً، قد لا تناسب وظيفة الأب احتياجات الابن تماماً. في هذه الحالة، يمكن للابن إعادة تعريف الدالة بنفس الاسم لتنفيذ كود مختلف.
class Shape:
def draw(self):
print("رسم شكل هندسي عام")
class Circle(Shape):
def draw(self):
print("رسم دائرة مثالية")
class Square(Shape):
def draw(self):
print("رسم مربع بأربعة أضلاع متساوية")
# تجربة الأشكال
shapes = [Circle(), Square(), Shape()]
for s in shapes:
s.draw()
شرح الكود سطراً بسطر:
class Shape:- نعرّف صنف أب عام للأشكال الهندسية.def draw(self):- دالة رسم عامة في الأب.class Circle(Shape):- صنف الدائرة يرث من Shape.def draw(self):- نعيد تعريف دالة draw بطريقة خاصة بالدائرة (Overriding).class Square(Shape):- صنف المربع يرث من Shape.def draw(self):- نعيد تعريف draw بطريقة خاصة بالمربع.shapes = [Circle(), Square(), Shape()]- ننشئ قائمة تحتوي على 3 كائنات مختلفة.for s in shapes:- نمر على كل شكل في القائمة.s.draw()- نستدعي نفس الدالة، لكن كل كائن ينفذها بطريقته الخاصة!
الفائدة: يمكننا معاملة جميع الأشكال بنفس الطريقة، لكن كل شكل يرسم نفسه بطريقته الفريدة.
المثال الرابع: خطأ شائع - نسيان super()
هذا مثال يوضح خطأ شائعاً يقع فيه المبتدئون عند تعلم الكود بالعربية، وهو نسيان استدعاء باني الأب.
# ❌ خطأ شائع
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, breed):
# نسينا استدعاء super().__init__()
self.breed = breed
dog = Dog("جيرمان شيبرد")
# print(dog.name) # خطأ! name غير معرف
# ✅ الطريقة الصحيحة
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name) # استدعاء باني الأب
self.color = color
cat = Cat("ميسي", "أبيض")
print(f"القطة: {cat.name}, اللون: {cat.color}")
شرح الكود سطراً بسطر:
class Animal:- صنف أب للحيوانات.def __init__(self, name):- الباني يستقبل اسم الحيوان.self.name = name- نحفظ الاسم.class Dog(Animal):- صنف الكلب يرث من Animal.def __init__(self, breed):- خطأ! عرّفنا باني جديد لكن نسينا استدعاء باني الأب.self.breed = breed- حفظنا السلالة فقط، لكن name لم يتم تعريفه!dog = Dog("جيرمان شيبرد")- ننشئ كلب.# print(dog.name)- لو حاولنا طباعة الاسم سنحصل على خطأ لأن name غير موجود!class Cat(Animal):- الطريقة الصحيحة: صنف القطة.super().__init__(name)- مهم! نستدعي باني الأب لتعريف name.self.color = color- ثم نضيف خاصية إضافية.cat = Cat("ميسي", "أبيض")- ننشئ قطة بنجاح.print(f"القطة: {cat.name}, اللون: {cat.color}")- الآن name و color كلاهما موجودان!
القاعدة الذهبية: إذا عرّفت __init__ في الابن، استدعِ دائماً super().__init__() أولاً.
5. أخطاء شائعة
عند تعلم البرمجة للمبتدئين خطوة بخطوة، يقع الكثيرون في هذه الأخطاء:
الخطأ 1: نسيان تمرير الأب في القوسين
كتابة class Child: بدلاً من class Child(Parent): تجعل الصنفين منفصلين تماماً ولا توجد وراثة.
الحل: تأكد دائماً من كتابة اسم الأب بين القوسين.
الخطأ 2: نسيان استدعاء باني الأب
إذا عرفت __init__ في الابن، فإنها تلغي باني الأب تماماً.
الحل: استخدم super().__init__() في أول سطر من باني الابن.
الخطأ 3: الوراثة العميقة جداً
إنشاء سلسلة وراثة طويلة جداً (أب → ابن → حفيد → ...) يجعل الكود معقداً وصعب التتبع.
الحل: حاول إبقاء الهيكل بسيطاً (3 مستويات كحد أقصى).
6. نصائح مهمة
قاعدة "Is-A" (هو نوع من)
استخدم الوراثة فقط إذا كان بإمكانك قول "الابن هو نوع من الأب". مثلاً: "الكلب هو حيوان" (صح)، "المحرك هو سيارة" (خطأ، المحرك جزء من السيارة وليس نوعاً منها).
اجعل الأب عاماً
الصنف الأب يجب أن يحتوي على الخصائص التي يشترك فيها الجميع، بينما الابن يحتوي على التفاصيل الدقيقة. هذا يجعل الكود أكثر مرونة.
التوثيق مهم
دائماً اذكر في تعليقاتك لماذا يرث هذا الصنف من ذاك، لتسهيل العمل الجماعي وفهم الكود لاحقاً.
7. تمرين عملي
تحدي نظام المكتبة
قم بإنشاء نظام بسيط للمكتبة يتبع الشروط التالية:
- أنشئ صنفاً أب يسمى
LibraryItemيحتوي على (العنوان، الكاتب). - أضف دالة
display_infoفي الأب لعرض البيانات. - أنشئ صنفاً ابناً يسمى
Bookيرث من الأب ويضيف خاصية (عدد الصفحات). - أنشئ صنفاً ابناً آخر يسمى
Magazineيرث من الأب ويضيف خاصية (رقم العدد). - قم بعمل Overriding لدالة العرض في الأبناء لتشمل الخصائص الجديدة.
حاول حل التمرين بنفسك قبل الانتقال للدرس القادم! هذا التمرين سيثبت فهمك للوراثة.
ملخص الدرس
- الوراثة تسمح لصنف بوراثة خصائص وأساليب صنف آخر.
- نستخدم الوراثة لتقليل تكرار الكود وتنظيمه بشكل هرمي.
- يمكن للابن تعديل أساليب الأب باستخدام Method Overriding.
- نستخدم
super()للوصول إلى محتويات الصنف الأب. - الوراثة هي أحد أعمدة البرمجة الكائنية الأربعة (مع التغليف وتعدد الأشكال).
الخطوة التالية
لقد قطعت شوطاً طويلاً في دورة تعلم البرمجة بالعربية. هل تعلم أن بايثون تسمح للابن بأن يرث من عدة آباء في نفس الوقت؟ لنكتشف هذا المفهوم المثير في الدرس القادم.
الدرس التالي: الوراثة المتعددة (Multiple Inheritance)