بايثونالوراثة
- مفهوم الوراثة
- طريقة جعل كلاس يرث من كلاس آخر
- أشكال الوراثة في بايثون
- معرفة طبيعة العلاقة بين الكلاسات و الكائنات
- تضارب الأسماء عند وراثة أكثر من كلاس
- حل مشكلة تضارب الأسماء
- الكلاس object
مفهوم الوراثة
الوراثة ( Inheritance ) تعني تضمين محتوى كلاس في كلاس آخر.
في بايثون، الكلاس يمكنه أن يرث من كلاس آخر و بالتالي يحصل على جميع الدوال و المتغيرات الموجودة فيه.
الوراثة يمكن الإستفادة منها للحد من تكرار الكود، فمثلاً إذا كنت تريد إنشاء كلاس جديد و كان لديك كلاس جاهز يحتوي على متغيرات و دوال تحتاج أن تكتبها فيه، فيمكنك جعل الكلاس يرثها كما هي بدلاً من إعادة تعريفها فيه، و بعدها يمكنك إستخدام جميع المتغيرات و الدوال التي ورثها الكلاس الجديد من الكلاس الجاهز.
مفهوم الكلاس الأب و الكلاس الإبن
- الكلاس الذي يرث من كلاس آخر يعتبر بمثابة إبن له لذلك يقال له Subclass أو Derived class أو Extended class أو Child class.
- الكلاس الذي يوّرث محتوياته لكلاس آخر يعتبر بمثابة أب له لذلك فيقال له Superclass أو Base class أو Parent class.
ما يرثه الكلاس الإبن من الكلاس الأب
إفتراضياً، الكلاس الإبن يرث كل الخصائص و الدوال الموجودة في الكلاس الأب ما عدا الخصائص التي يتم تعريفها فيه كباراميترات في الدالة __init__() فيه لأن هذه الدالة يتم إستدعاؤها عند إنشاء كائن منه و ليس عند وراثته.
الكلاس الإبن يمكنه استدعاء الدالة __init__() الموجودة في الكلاس الأب بواسطة الدالة super() التي سنتعرف عليها لاحقاً.
طريقة جعل كلاس يرث من كلاس آخر
لجعل الكلاس يرث من كلاس آخر، نضع بعد إسم الكلاس قوسين و بداخلهما إسم الكلاس الذي نريده أن يرث منه.
{ClassBody}
- ChildClass — يقصد بها إسم الكلاس الإبن.
- ParentClass — يقصد بها إسم الكلاس الأب.
- {ClassBody} — يقصد بها الأشياء الخاصة بالكلاس الإبن إضافةً إلى الأشياء التي ورثها من الكلاس الأب.
في حال كان الكلاس يرث من أكثر من كلاس فإنه يجب وضع فاصلة بين كل كلاسَين نضعهما بين القوسين.
أشكال الوراثة في بايثون
في بايثون يوجد 4 أشكال للوراثة كما هو موضح في الجدول التالي.
إذاَ أشكال الوراثة في بايثون هي كالتالي:
- وراثة فردية — تعني كلاس يرث من كلاس واحد فقط.
- وراثة متتالية — تعني كلاس يرث من كلاس و الذي بدوره يرث من كلاس آخر.
- وراثة هرمية — تعني أن الكلاس موروث من قبل أكثر من كلاس.
- وراثة متعددة — تعني أن الكلاس يرث من أكثر من كلاس.
مثال عن الوراثة الفردية
في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على متغير إسمه x و دالة إسمها print_msg().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على متغير إسمه y و يرث من الكلاس A.
مثال
نتيجة تشغيل الملف Test
.
x: 10
Hello from class A
مثال عن الوراثة المتتالية
في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها print_a().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها print_b() و يرث من الكلاس A.
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها print_c() و يرث من الكلاس B.
المثال الثاني
نتيجة تشغيل الملف Test
.
Hello from class B
Hello from class C
مثال عن الوراثة المتعددة
في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها print_a().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها print_b().
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها print_c() و يرث من الكلاس A و الكلاس B.
المثال الثالث
نتيجة تشغيل الملف Test
.
Hello from class B
Hello from class C
مثال عن الوراثة الهرمية
في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها print_a().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها print_b() و يرث من الكلاس A.
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها print_c() و يرث من الكلاس A أيضاً.
المثال الرابع
نتيجة تشغيل الملف Test
.
Hello from class C
Hello from class A
Hello from class B
معرفة طبيعة العلاقة بين الكلاسات و الكائنات
الجدول التالي يحتوي على دوال جاهزة في بايثون يمكن إستخدامها لمعرفة طبيعة العلاقة بين كلاس و آخر أو بين كائن و آخر.
ملاحظة: في الأمثلة الموضوع إستخدمنا الكلمة المفتاحية pass من أجل تعريف كلاسات فارغة.
إسم الدالة مع تعريفها | |
---|---|
1 | issubclass(subclass, superclass)
تستخدم لمعرفة ما إذا كان الكلاس الذي نمرره مكان الباراميتر subclass يرث من الكلاس الذي نمرره مكان الباراميتر superclass أم لا. ترجع True في حال كان كذلك، و غير ذلك ترجع False. شاهد المثال |
2 | isinstance(obj, classinfo)
تستخدم لمعرفة ما إذا كان الكائن الذي نمرره مكان الباراميتر obj تم إنشاؤه من الكلاس الذي نمرره مكان الباراميتر classinfo أم لا. ترجع True في حال كان كذلك، و غير ذلك ترجع False. شاهد المثال |
تضارب الأسماء عند وراثة أكثر من كلاس
في حال تم جعل الكلاس يرث من أكثر من كلاس في وقت واحد فإنه فيوجد إحتمال كبير أن يرث منهم خصائص و دوال عندها نفس الإسم و بالتالي سيحدث تضارب في الأسماء.
تضارب الأسماء الذي قد يحصل لا يسبب خطأ في الكود عند التشغيل لكنه قد يؤدي لحدوث خطأ منطقي في النتيجة المرجو الحصول عليها.
في حال كان الكلاس يرث من كلاسيَن و كان يوجد في كلاهما متغير عنده نفس الإسم و دالة عندها نفس الإسم، سيقوم مفسّر لغة بايثون بتوريث الكلاس المتغير و الدالة الموجودين في أول كلاس يرث منه. لأن أول كلاس يتم الوراثة منه يتم إعطاؤه أولوية التنفيذ في حال حدث تضارب في الأسماء.
في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها print_msg().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها print_msg() أيضاً.
بعدها قمنا بإنشاء كلاس إسمه C يرث من الكلاس A و الكلاس B و يحتوي على دالة إسمها call_print_msg().
مثال
نتيجة تشغيل الملف Test
.
حل مشكلة تضارب الأسماء
لإنشاء كلاس مع ضمان عدم حدوث مشكلة في تضارب الأسماء عند الوراثة منه، يمكن إعتماد مبدأ إخفاء البيانات ( Data Hiding ) عند تسمية المتغيرات و الدوال. إخفاء البيانات يجعل المبرمج قادر على وضع استخدام أسماء المتغيرات و الدوال نفسها في الكلاس الأب و الكلاس الإبن مع ضمان عدم حدوث أي تضارب في الأسماء.
إخفاء البيانات يتم من خلال وضع الرمز __ في بداية إسم المتغير أو الدالة و عندها يتولى مفسّر لغة بايثون التفريق بين الأشياء الموجودة في الكلاس الأب و الأشياء الموجودة في الكلاس الإبن.
الوصول للبيانات المخفية
عند وراثة شيء مخفي سواء كان دالة أو متغير فإنك تصل له على النحو التالي:
- ClassName: مكانه نضع إسم الكلاس الذي يوجد فيه المتغير أو الدالة المخفية.
- IdentifierName: مكانه نضع إسم المتغير أو الدالة المخفية الموجودة في الكلاس.
على سبيل المثال، إذا قمنا بإنشاء كلاس إسمه A و ووضعنا فيه متغير خفي إسمه __x، فإنه عند الوراثة من هذا الكلاس يمكن الوصول لهذا المتغير هكذا _A__x.
إذاً مفسر بايثون يعيد تسمية المتغير الخفي نسبة لإسم الكلاس و هكذا يضمن أن لا يحدث تضارب في الأسماء.
في المثال التالي قمنا بتعريف كلاس يرث من كلاس و كلاهما يحتويان على متغير خفي إسمه __x و دالة خفية إسمها __print_msg().
بعدها قمنا بإنشاء كائن من الكلاس الإبن و فيه عرضنا قيمة كل المتغيرات الخفية الموجودة فيه و قمنا باستدعاء الدوال الخفية أيضاَ.
مثال
نتيجة تشغيل الملف Test
.
__x obtained from class B = 20
Hello from class A
Hello from class B
الكلاس object
في بايثون، أي كلاس جاهز أو تنشئه بنفسك يرث بشكل تلقائي من كلاس إسمه object.
الكلاس object يعتبر Superclass لجميع الكلاسات في بايثون، و بالتالي فإن أي كائن ننشئه سيحتوي على كل الخصائص و الدوال الموجودة فيه.
الآن، إذا قمنا بتعريف كلاس إسمه Test لا يرث من أي كلاس.
فكن على يقين أن مفسّر لغة بايثون سيجعل الكلاس Test يرث بشكل تلقائي من الكلاس object كالتالي.
و بالنسبة للدوال التي تظهر لك أثناء كتابة الكود بمجرد أن تضع نقطة بعد إسم الكائن و التي لم تقم بتعريفها بنفسك، فهي وجدت في الكائن لأنه تم إنشاء هذا الكائن في الأساس من كلاس يرث من الكلاس object.
أين نجد الكلاس object
الكلاس object موضوع بداخل موديول إسمه builtins.py.
هذا الموديول يقوم مفسّر بايثون بتضمينه بشكل تلقائي في كل كلاس و لهذا السبب لا نحتاج أن نفعل له import.
الموديول builtins.py يحتوي على كل الدوال التي كنا نستخدمها سابقاً و التي كنا نقول أنها دوال جاهزة في بايثون، و هو يحتوي أيضاً على كلاسات كثيرة غير الكلاس object لكننا لن نتطرق إليها في هذا الدرس.
الجدول التالي يحتوي على خصائص الكلاس object.
إسم الخاصية مع تعريفها | |
---|---|
1 | __class__ ترجع إسم الكلاس الذي أنشئ منه الكائن الذي قام باستدعائها. |
2 | __dict__ ترجع كائن dict فيه كل خصائص الكلاس الذي أنشئ منه الكائن الذي قام باستدعائها. |
3 | __doc__ ترجع التفسير ( Documentation ) الموضوع في الكلاس الذي أنشئ منه الكائن الذي قام باستدعائها. |
4 | __module__ ترجع إسم الموديول الذي يحتوي على الكلاس الذي أنشئ منه الكائن الذي قام باستدعائها. |
الجدول التالي يحتوي على بعض دوال الكلاس object.
إسم الدالة مع تعريفها | |
---|---|
1 | __init__()
تستخدم هذه الدالة لتعريف خصائص للكائن و إعطائهم قيم أولية. يتم إستدعاء هذه الدالة بشكل تلقائي عند إنشاء كائن من الكلاس الذي يحتويها. |
2 | __del__()
تستخدم هذه الدالة لتنفيذ أوامر معينة عندما يتم مسح الكائن من الذاكرة. يتم إستدعاء هذه الدالة بشكل تلقائي عند مسح كائن من الكلاس الذي يحتويها. |
3 | __sizeof__() ترجع عدد صحيح يمثل حجم الكائن (أي المساحة التي يحتلها) في الذاكرة بالـ Byte. |
4 | __repr__()
ترجع نص يمثل شكل الكائن كما الذي قام باستدعائها في الذاكرة مع احترام نوع و قيمة الكائن. ملاحظة: استدعاء هذه الدالة مطابق لاستدعاء دالة إسمها repr(object) موضوعة مباشرةً في الموديول builtins.py. |
5 | __str__()
ترجع نص يمثل شكل الكائن كما الذي قام باستدعائها في الذاكرة. ملاحظة: استدعاء هذه الدالة مطابق لاستدعاء دالة إسمها str(object) موجودة مباشرةً في الموديول builtins.py. |
في المثال التالي قمنا بتعريف كلاس إسمه Car وضعنا فيه الدالة __init__() و الدالة __del__().
بعدها قمنا بإنشاء كائن منه و استدعاء الخصائص و الدوال التي ورثها الكلاس Car في الأساس من الكلاس object.
المثال الأول
نتيجة تشغيل الملف Test
.
Object module name: Car
Object class documentation: This class represent any car object
Object properties and values: {'model': 'BMW', 'year': '2015', 'color': 'Black', 'price': 22000}
Object size in Byte: 16
Object representation: <Car.Car object at 0x01080A30>
Object string representation: <Car.Car object at 0x01080A30>
Object is destroyed
لعلك تتساءل ما الفرق بين الدالة __repr__() و __str__() حيث أنك لم تلاحظ أي فرق بينهما في المثال السابق.
المثال التالي يوضح لك الفرق بينهما.
المثال الثاني
نتيجة تشغيل الملف Test
.
Object string representation: Python Tutorial