مفهوم التجريد في بايثون
التجريد ( Abstraction ) أسلوب مهم جداً في البرمجة و يتم إعتماده بشكل عام لتسهيل كتابة الأوامر على المبرمج و تنظيم عمله, فهو يجعله قادراً على تنفيذ ما يريد دون الحاجة إلى معرفة كافة التفاصيل التي تم فيها ذلك, و بالتالي جعله يتعامل مع الأشياء بسطحية بدل أن يغوص في معرفة تفاصيل الكودات المعقدة.
فمثلاً إذا كنت تنوي بناء تطبيق فيه واجهة مستخدم و هذا التطبيق يحتوي على زر معين, عند النقر على هذا الزر يتم إرسال رسالة إلكترونية إلى صاحب التطبيق. في هذه الحالة لن يكون عليك القلق بتاتاً حول الطريقة التي سيتم فيها ربط الكود الذي يسمح بإرسال الرسالة بالزر الموجود في واجهة المستخدم, لأن مطوري واجهة المستخدم يطلبوا منك تعريف دالة لها شكل معين و وضع الكود الخاص بإرسال الرسالة فيها, و من ثم تمرير هذه الدالة إلى الكائن الذي يمثل الزر الذي يظهر في واجهة المستخدم. عندها سيتم تنفيذ الكود الموجود في الدالة في كل مرة يتم فيها النقر على الزر.
إذاً التجريد هو أسلوب يقضي بتعريف دوال مجرّدة ( Abstract Methods ) في الكلاس الأب و إجبار الكلاس الإبن على أن يفعل Override لهذه الدوال بالشكل الذي يناسبه.
و بالتالي عندما تنشئ كلاس يرث من كلاس فيه دوال مجرّدة سيظهر أمامك تحذير ينبهك أنك يجب أن تفعل Override لهذه الدوال حتى لا يظهر لك خطأ وقت التشغيل.
مصطلحات تقنية
الكلاس العادي يقال له Concrete Class.
الكلاس المعرف ككلاس مجرّد يقال له Abstract Class.
الدالة المعرفة كدالة مجرّدة يقال لها Abstract Method أو Function Method.
تعريف Abstract Class في بايثون
لتحويل الكلاس العادي إلى كلاس مجرّد, يجب جعل الكلاس يرث من كلاس إسمه ABC الموجود في الأساس في موديول جاهز إسمه abc.
معلومة: الكلاس ABC تم تسميته هكذاً إختصاراً لجملة ( Abstract Base Classes ) التي تعني الكلاس الأب لجميع الكلاسات المجرّدة.
نقاط مهمة حول الكلاس المجرّد
الكلاس المجرّد يمكنه أن يحتوي على دوال عادية, و يمكنه أن يحتوي على دوال نوعها Abstract.
الكلاس المجرّد لا يمكن إنشاء كائنات منه في حال كان يحتوي على دوال نوعها Abstract.
بما أنه لا يمكن إنشاء كائنات من الكلاس المجرّد في جميع الحالات, فهذا يعني أن الإستفادة الحقيقية من هذا الكلاس تكون بوراثته.
الكلاس الذي يرث من كلاس مجرّد مجبر على أن يفعل Override لجميع الدوال المعرفة فيه Abstract.
تعريف كلاس مجرّد
في المثال التالي قمنا بتعريف كلاس مجرّد إسمه ParentClass يحتوي على دالة إسمها print_msg.
بعدها قمنا بإنشاء كلاس إسمه ChildClass يرث من الكلاس ParentClass.
ملاحظة: الكلاس ChildClass غير مجبر على أن يفعل Override للدالة print_msg() لأنها ليست معرفة كدالة مجرّدة.
مثال
# حتى نستطيع الوراثة منه abc الموجود في الموديول ABC هنا قمنا بتضمين الكلاس
from abc import ABC
# حتى يصبح كلاس مجرّد ABC و جعلناه يرث من الكلاس ParentClass هنا قمنا بإنشاء كلاس إسمه
class ParentClass(ABC):
# هنا قمنا بتعريف دالة عادية تطبع جملة عند إستدعاءها
def print_msg(self):
print('Normal method defined in an abstract class')
# حتى نستطيع الوراثة منه ParentClass الموجود في الموديول ParentClass هنا قمنا بتضمين الكلاس from ParentClass import ParentClass # ParentClass يرث من الكلاس ChildClass هنا قمنا بإنشاء كلاس فارغ إسمه class ChildClass(ParentClass): pass
# حتى نستطيع إنشاء كائن منه ChildClass هنا قمنا بتضمين الكلاس from ChildClass import ChildClass # منه print_msg() بعدها قمنا باستدعاء الدالة .obj إسمه ChildClass هنا قمنا بإنشاء كائن من الكلاس obj = ChildClass() obj.print_msg()
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
ملاحظة
في المثال السابق كنت تستطيع إنشاء كائن من الكلاس ParentClass لأنه لا يحتوي على دوال مجرّدة.
لكن كما سبق و قلنا أنه بشكل عام يتم إنشاء الكلاس المجرّد في الأساس حتى يتم وراثته و ليس من أجل إنشاء كائنات منه.
تعريف Abstract Method في بايثون
طريقة تعريف كلاس مجرد يرث من كلاس مجرد في بايثون
إذا كنت تريد بناء دالة فارغة و جعل الكلاس الذي يرثها مجبر و مسؤول عن كتابة محتواها, قم بوضع الكلمة @abstractmethod فوقها و فيها ضع الكلمة pass فقط.
و إذا كنت تريد بناء دالة و جعل الكلاس الذي يرثها مجبر على تعريفها من جديد مع إمكانية الإستفادة من الكود الموجودة فيها, قم بوضع الكلمة @abstractmethod فوقها فقط.
نقاط مهمة حول الدوال المجرّدة
إذا وضعت الكلمة
@abstractmethodفوق الدالة, فهذا يعني أنها دالة مجرّدة و ليس دالة عادية.الكلاس الذي يرث من كلاس مجرّد, يجب أن يفعل Override لكل الدوال المجرّدة التي ورثها منه.
تعريف دوال مجرّدة
في المثال التالي قمنا بتعريف كلاس مجرّد إسمه ParentClass يحتوي على 3 دوال كالتالي:
دالة عادية إسمها
m1.دالة مجردة و فارغة إسمها
m2.دالة مجردة و فيها محتوى إسمها
m3.
بعدها قمنا بإنشاء كلاس إسمه ChildClass يرث من الكلاس ParentClass.
ملاحظة: الكلاس ChildClass مجبر على أن يفعل Override لكل دالة مجرّدة ورثها من الكلاس ParentClass.
مثال
# حتى نستطيع استخدامهما abc الموجودين في الموديول @abstractmethod و الكلمة ABC هنا قمنا بتضمين الكلاس
from abc import ABC, abstractmethod
# حتى يصبح كلاس مجرّد ABC و جعلناه يرث من الكلاس ParentClass هنا قمنا بإنشاء كلاس إسمه
class ParentClass(ABC):
# m1 هنا قمنا بتعريف دالة عادية إسمها
def m1(self):
print('m1: Normal method defined by ParentClass')
# m2 هنا قمنا بتعريف دالة مجردة و فارغة إسمها
@abstractmethod
def m2(self):
pass
# m3 هنا قمنا بتعريف دالة مجردة و فيها أمر طباعة إسمها
@abstractmethod
def m3(self):
print('m3: Default content is written by ParentClass')
# حتى نستطيع الوراثة منه ParentClass الموجود في الموديول ParentClass هنا قمنا بتضمين الكلاس
from ParentClass import ParentClass
# ParentClass يرث من الكلاس ChildClass هنا قمنا بإنشاء كلاس إسمه
class ChildClass(ParentClass):
# ParentClass لأن هذه الدالة كانت معرفة كدالة مجرّدة في الكلاس m2() للدالة Override هنا فعلنا
def m2(self):
print('m2: Its content is written by ChildClass')
# ParentClass لأن هذه الدالة كانت معرفة كدالة مجرّدة في الكلاس m3() للدالة Override هنا فعلنا
def m3(self):
super().m3() # و بعدها قمنا بإضافة أمر طباعة ParentClass الموجودة في الكلاس m3() لاحظ أننا قمنا باستدعاء الدالة
print('m3: ChildClass add his own code too')
# حتى نستطيع إنشاء كائن منه ChildClass هنا قمنا بتضمين الكلاس from ChildClass import ChildClass # obj إسمه ChildClass هنا قمنا بإنشاء كائن من الكلاس obj = ChildClass() # obj هنا قمنا باستدعاء الدوال الثلاثة الموجودة في الكائن obj.m1() obj.m2() obj.m3()
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
m2: Its content is written by ChildClass
m3: Default content is written by ParentClass
m3: ChildClass add his own code too
أمثلة تطبيقية على التجريد في بايثون
مثال يوضح الفائدة من تعريف كلاس مجرّد في بايثون
في المثال التالي قمنا بتعريف كلاس مجرّد إسمه A وضعنا فيه دالة مجرّدة إسمها m1.
بعدها قمنا بتعريف كلاس مجرّد إسمه B يرث من الكلاس A و وضعنا فيه دالة مجرّدة إسمها m2.
بعدها قمنا بتعريف كلاس عادي إسمه C يرث من الكلاس B و يفعل Override للدالتين m1() و m2().
في الأخير قمنا بإنشاء كائن من الكلاس C و استدعاء كلا الدالتين منه.
مثال
# حتى نستطيع استخدامهما abc الموجودين في الموديول @abstractmethod و الكلمة ABC هنا قمنا بتضمين الكلاس
from abc import ABC, abstractmethod
# حتى يصبح كلاس مجرّد ABC و جعلناه يرث من الكلاس A هنا قمنا بإنشاء كلاس إسمه
class A(ABC):
# m1 هنا قمنا بتعريف دالة مجردة و فارغة إسمها
@abstractmethod
def m1(self):
pass
# حتى نستطيع الوراثة منه A حتى نستطيع تعريف دالة مجرّدة + الكلاس @abstractmethod هنا قما بتضمين الكلمة
from abc import abstractmethod
from A import A
# m1() حتى يصبح كلاس مجرّد و يحتوي على الدالة ABC الذي بدوره يرث من الكلاس A و جعلناه يرث من الكلاس B هنا قمنا بإنشاء كلاس إسمه
class B(A):
# m2 هنا قمنا بتعريف دالة مجردة و فارغة إسمها
@abstractmethod
def m2(self):
pass
# أيضاَ A من الكلاس m1() ورث الدالة المجردة B لا تنسى أن الكلاس
# حتى نستطيع الوراثة منه B.py الموجود في الملف B هنا قمنا بتضمين الكلاس
from B import B
# لجميع الدوال المجرّردة التي ورثها Override و بالتالي أصبح هذا الكلاس مجبراً على أن يفعل B يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه
class C(B):
# m1() للدالة Override هنا فعلنا
def m1(self):
print('m1 content is written in the class c')
# m2() للدالة Override هنا فعلنا
def m2(self):
print('m2 content is written in the class c')
# حتى نستطيع إنشاء كائن منه ChildClass هنا قمنا بتضمين الكلاس
from C import C
# obj إسمه C هنا قمنا بإنشاء كائن من الكلاس
obj = C()
# obj هنا قمنا باستدعاء كلا الدالتين الموجودتين في الكائن
obj.m1()
obj.m2()
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
m2 content is written in the class c
في المثال التالي قمنا ببناء كلاس مجرّد إسمه Person يحتوي على الدالة __init__() التي وضعنا فيها 4 خصائص إسمهم name, gender, brithday و is_married, بالإضافة إلى دالة مجرّدة و فارغة إسمها print_info().
بعدها قمنا بتعريف كلاس إسمه Student يرث من الكلاس Person و يملك خاصية إضافية إسمها specialization.
بعدها قمنا بتعريف كلاس إسمه Employee يرث من الكلاس Person و يملك خاصية إضافية إسمها salary.
الفكرة هنا أن أي كلاس سيتم إنشاءه لتمثيل إنسان يجب أن يرث من الكلاس Person الذي يملك الخصائص المشتركة لكل البشر و يجب أن يفعل Override للدالة print_info() حتى تعرض كل قيم الخصائص الموجودة في الكلاس الذي ورثها بشكل مرتب.
في الأخير قمنا بإنشاء كائن من الكلاس Student و كائن من الكلاس Employee و إستدعاء الدالة print_info() من كل واحد منهما.
مثال
# حتى نستطيع استخدامهما abc الموجودين في الموديول @abstractmethod و الكلمة ABC هنا قمنا بتضمين الكلاس
from abc import ABC, abstractmethod
# هذا الكلاس يعتبر الكلاس الأساسي لأي إنسان حيث أن كل إنسان يملك معلومات أساسية مثل: إسم, جنس, تاريخ ميلاد و الوضع الإجتماعي
# حتى يصبح كلاس مجرّد ABC جعلنا هذا الكلاس يرث من الكلاس
class Person(ABC):
# التي نريد أن يتمكلها أي شخص يتم تعريفه Person التي وضعنا فيها خصائص الكلاس __init__() هنا قمنا بتعريف الدالة
def __init__(self, name, gender, birthday, is_married):
self.name = name
self.gender = gender
self.birthday = birthday
self.is_married = is_married
# الكلاس الذي يرثها ليحدد كيف يريد أن يتم عرض خصائص الأشخاص Override هذه الدالة يجب أن يفعل لها
@abstractmethod
def print_info(self):
pass
# حتى نستطيع الوراثة منه Person.py الموجود في الملف Person هنا قمنا بتضمين الكلاس
from Person import Person
# لأنه يمكنه الإستفادة منه Person يعتبر الكلاس الأساسي لإنشاء أي طالب, هذا الكلاس جعلناه يرث من الكلاس Student هنا قمنا بتعريف كلاس إسمه
class Student(Person):
# و التي سيملكها أي كائن ننشئه منه Student التي وضعنا فيها خصائص الكلاس __init__() هنا قمنا بتعريف الدالة
def __init__(self, name, gender, birthday, is_married, specialization):
# التي ندخلها عند إنشاء كائن name و gender, birthday, is_married و مررنا لها قيم الباراميترات Person الموجودة في الكلاس __init__() هنا قمنا باستدعاء الدالة
super().__init__(name, gender, birthday, is_married)
# التي ذكرناها في هذا الكلاس سيتم إعطائها آخر قيمة نمررها في أقواس الكائن الذي ننشئه من هذا الكلاس specialization الخاصية
self.specialization = specialization
# Student لتحديد كيف سيتم طباعة كل قيم الخصائص التي يمتكلها أي كائن ننشئه من الكلاس print_info() للدالة Override هنا فعلنا
def print_info(self):
print('name:', self.name)
print('gender:', self.gender)
print('birthday:', self.birthday)
print('is married:', self.is_married)
print('specialization:', self.specialization)
print('-----------------------------')
# حتى نستطيع الوراثة منه Person.py الموجود في الملف Person هنا قمنا بتضمين الكلاس
from Person import Person
# لأنه يمكنه الإستفادة منه Person يعتبر الكلاس الأساسي لإنشاء أي طالب, هذا الكلاس جعلناه يرث من الكلاس Employee هنا قمنا بتعريف كلاس إسمه
class Employee(Person):
# و التي سيملكها أي كائن ننشئه منه Employee التي وضعنا فيها خصائص الكلاس __init__() هنا قمنا بتعريف الدالة
def __init__(self, name, gender, birthday, is_married, salary):
# التي ندخلها عند إنشاء كائن name و gender, birthday, is_married و مررنا لها قيم الباراميترات Person الموجودة في الكلاس __init__() هنا قمنا باستدعاء الدالة
super().__init__(name, gender, birthday, is_married)
# التي ذكرناها في هذا الكلاس سيتم إعطائها آخر قيمة نمررها في أقواس الكائن الذي ننشئه من هذا الكلاس salary الخاصية
self.salary = salary
# Employee لتحديد كيف سيتم طباعة كل قيم الخصائص التي يمتكلها أي كائن ننشئه من الكلاس print_info() للدالة Override هنا فعلنا
def print_info(self):
print('name:', self.name)
print('gender:', self.gender)
print('birthday:', self.birthday)
print('is married:', self.is_married)
print('salary:', self.salary)
print('-----------------------------')
# حتى نستطيع إنشاء كائنات منهما Employee.py الموجود في الملف Employee و الكلاس Student.py الموجود في الملف Student هنا قمنا بتضمين الكلاس
from Employee import Employee
from Student import Student
# e إسمه Employee و كائن من الكلاس s إسمه Student هنا قمنا بإنشاء كائن من الكلاس
s = Student('Mhamad', 'Male', '1994', False, 'Computer Science')
e = Employee('Rana', 'Female', '1986', True, 1500)
# حتى تعرض قيمهم بشكل مرتب s و e الموجودة في كل من الكائن print_info() هنا قمنا باستدعاء الدالة
s.print_info()
e.print_info()
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
gender: Male
birthday: 1994
is married: False
specialization: Computer Science
-----------------------------
name: Rana
gender: Female
birthday: 1986
is married: True
salary: 1500
-----------------------------
سترى فائدة الـ Abstraction أيضاً في دروس متقدمة عندما تستخدم كلاسات جاهزة تتيح لك التعامل مع الشبكات (Networks), الواجهات (GUI) و قواعد البيانات (DataBases) بكل سهولة. كما أنك ستراها عندما تعمل على بناء مشاريع كبيرة, تجبرك على إستخدام هذا الأسلوب لتسهيل العمل في المشروع.
مفهوم تعدد الأشكال في بايثون
تعدد الأشكال أو بوليمورفيزم ( Polymorphism ) هو مجرد أسلوب في كتابة الكود يقصد منه بناء دالة تنفذ أوامر مختلفة على حسب الكائن الذي نمرره لها عند إستدعاءها.
في العادة تعدد الأشكال يكون مرتبط بشكل أساسي بالوراثة حيث تكون الدالة مبنية على أساس الكلاس الأب, و لكننا عند إستدعاءها نمرر لها كائن من إحدى الكلاسات التي ترث منه.
إذاً, تعدد الأشكال يمكن أن يتحقق بأشكال مختلفة على حسب الكود الذي تبنيه أو تستخدمه.
و أحياناً تجد نفسك تستخدم نفس الدالة مع مصفوفة أو مع نص, فتجد أن طريقة عمل هذه الدالة و ما ترجعه لك يختلف تماماً على حسب نوع الكائن الذي تمرره لها.
دوال جاهزة في بايثون تطبق مبدأ تعدد الأشكال
من أبرز الدوال التي نستخدمها و التي تطبق مبدأ تعدد الأشكال هي الدالة len() التي سبق أن تعاملنا معها في أكثر من درس و لكن كل مرة كنا نستخدمها لسبب مختلف.
فمثلاً, عند تمرير نص لهذه الدالة فإنها ترجع عدد أحرفه. و عند تمرير مصفوفة لهذه الدالة فإنها ترجع عدد عناصرها.
في المثال التالي قمنا بتعريف كائن إسمه string وضعنا فيه نص عادي, و كائن نوعه list إسمه aList وضعنا فيه 5 عناصر.
بعدها قمنا بطباعة ما سترجعه الدالة len() في حال تمرير الكائن string و الكائن aList لها.
مثال
# يحتوي على نص string هنا قمنا بتعريف كائن إسمه
string = 'Python tutorial for beginners'
# وضعنا فيه مجموعة أعداد صحيحة aList إسمه list هنا قمنا بتعريف
aList = [10, 20, 30, 40, 50]
# len() الذي سترجعه الدالة string هنا قمنا بعرض عدد أحرف الكائن
print('Number of characters in string is:', len(string))
# len() الذي سترجعه الدالة aList هنا قمنا بعرض عدد عناصر الكائن
print('Number of elements in aList is:', len(aList))
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
Number of elements in aList is: 5
إذاً, نلاحظ أنه عند تمرير الكائن string للدالة len() قامت بإرجاع عدد أحرفه. و عند تمرير الكائن aList قامت بإرجاع عدد عناصره. مما يعني أن الدالة len() تقوم بالتشييك على نوع الكائن الذي يمرر لها و على أساس نوعه تقوم بإرجاع القيمة.
معلومة تقنية
في بايثون, يوجد دالة جاهزة إسمها isinstance() يمكن استخدامها لمعرفة ما إذا كان الكائن قد تم إنشاؤه من كلاس معين أم لا.
في حال كان منشئاً منه فإنها ترجع True, أما إذا لم يكن منشئاً منه فإنها ترجع False.
الآن, بما أن الدالة len() تقوم بالتشييك على نوع الكائن الذي نمرره لها فهذا يعني أنها على الأرجح معرفة كالتالي.
def len(obj): # str هو obj هنا كأننا قلنا: هل نوع الكائن الذي تم تمرير مكان الكائن if isinstance(obj, str): # أي نص, سيتم تنفيذ الكود الخاص بحساب عدد أحرفه و من ثم إرجاعه str إذا كان نوع الكائن الذي تم تمريره للدالة هو # list هو objهنا كأننا قلنا: هل نوع الكائن الذي تم تمرير مكان الكائن elif isinstance(obj, list): # أي مصفوفة, سيتم تنفيذ الكود الخاص بحساب عدد عناصرها و من ثم إرجاعه list إذا كان نوع الكائن الذي تم تمريره للدالة هو
بناء دالة تطبق مبدأ تعدد الأشكال في بايثون
في المثال التالي قمنا بتعريف دالة إسمها print_sum() مصممة للتعامل مع ثلاث أنواع من الكائنات.
إذا مررت لها عدد صحيح, أي نوعه
int, فإنها تقوم بطباعة ناتج جمع الأرقام الموجودة فيه.إذا مررت لها نص يمثل عدد صحيح, أي نوعه
str, فإنها تقوم بتحويل الأحرف الموجودة فيه لأرقام و من ثم تقوم بطباعة ناتج جمع هذه الأرقام.إذا مررت لها مصفوفة نوعها
listتحتوي على أعداد صحيحة أو نصوص تمثل أعداد صحيحة, فإنها تقوم بطباعة ناتج جمع هذه العناصر.
مثال
# obj تحتوي على باراميتر إسمه print_sum هنا قمنا بتعريف دالة إسمها
def print_sum(obj):
# سنستخدمه عند إجراء أي عملية جمع لذلك قمنا بتجهيزه و إعطاءه القيمة 0 s المتغير
s = 0
# أي إذا كانت قيمته عبارة عن عدد صحيح ,int إذا كان قيمته نوعها obj الكود التالي مهمته حساب ناتج جمع الأرقام الموجودة في الكائن
if isinstance(obj, int):
while obj:
s += obj % 10
obj //= 10
print('Sum of digits:', s)
# أي إذا كانت قيمته عبارة عن نص يمثل عدد صحيح ,str إذا كان قيمته نوعها obj الكود التالي مهمته حساب ناتج جمع الأرقام الموجودة في الكائن
elif isinstance(obj, str):
for c in obj:
s += int(c)
print('Sum of characters:', s)
# أي إذا كانت قيمته عبارة عن مصفوفة ,list إذا كان عبارة عن obj الكود التالي مهمته حساب ناتج جمع قيم العناصر الموجودة في الكائن
elif isinstance(obj, list):
for e in obj:
s += int(e)
print('Sum of elements:', s)
# list أو str أو int ليس obj الكود التالي مهتمه طباعة جملة تنبيه في حال كان نوع قيمة الكائن
else:
print('This function is not made for this type!')
print_sum(12345) # و تمرير عدد صحيح لها print_sum() هنا قمنا باستدعاء الدالة
print_sum('12345') # و تمرير نص يمثل عدد صحيح لها print_sum() هنا قمنا باستدعاء الدالة
print_sum([1, 2, 3, 4, 5]) # لها list و تمرير مجموعة أعداد كـ print_sum() هنا قمنا باستدعاء الدالة
print_sum((1, 2, 3, 4, 5)) # لها tuple و تمرير مجموعة أعداد كـ print_sum() هنا قمنا باستدعاء الدالة
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
Sum of characters: 15
Sum of elements: 15
This function is not made for this type!
تطبيق مبدأ تعدد الأشكال مع الوراثة في بايثون
في المثال التالي قمنا بتعريف كلاس مجرّد إسمه BaseCountry يعتبر الكلاس الأساسي لأي كلاس يمثل بلد و بالتالي أي كلاس سننشئه ليمثل بلد ما يجب أن يرث منه. في هذا الكلاس قمنا بتجهيز 3 دوال مجرّدة أيضاً.
بعدها قمنا بتعريف كلاس إسمه Egypt و كلاس إسمه Australia يرثان من الكلاس BaseCountry و يفعلان Override لكل الدوال التي ورثوها منه.
بعدها قمنا بإنشاء دالة إسمها print_country_info() مهمتها إستدعاء جميع الدوال الموجودة في الكائن الذي نمرره لها بشرط أن يكون هذا الكائن قد تم إنشاؤه من كلاس يرث من الكلاس BaseCountry.
في الأخير قمنا بإنشاء كائن من الكلاس Egypt و كائن من الكلاس Australia و تمرير كل كائن منهما للدالة print_country_info().
مثال
# يحتوي على 3 دوال فارغة BaseCountry هنا قمنا بإنشاء كلاس إسمه class BaseCountry: def name(self): pass def capital(self): pass def language(self): pass
# حتى نستطيع الوراثة منه BaseCountry الموجود في الموديول BaseCountry هنا قمنا بتضمين الكلاس
from BaseCountry import BaseCountry
# للدوال الثلاثة الموجودة فيه Override و يفعل BaseCountry يرث من الكلاس Egypt هنا قمنا بإنشاء كلاس فارغ إسمه
class Egypt(BaseCountry):
def name(self):
print('Country: Egypt')
def capital(self):
print('Capital: Cairo')
def language(self):
print('Language: Arabic')
# حتى نستطيع الوراثة منه BaseCountry الموجود في الموديول BaseCountry هنا قمنا بتضمين الكلاس
from BaseCountry import BaseCountry
# للدوال الثلاثة الموجودة فيه Override و يفعل BaseCountry يرث من الكلاس Australia هنا قمنا بإنشاء كلاس فارغ إسمه
class Australia(BaseCountry):
def name(self):
print('Country: Australia')
def capital(self):
print('Capital: Canberra')
def language(self):
print('Language: English')
# حتى نستطيع التعامل معهم Australia و Egypt ,BaseCountry هنا قمنا بتضمين الكلاسات
from BaseCountry import BaseCountry
from Egypt import Egypt
from Australia import Australia
# obj عند استدعاء هذه الدالة, إذا كان الكائن الذي تم تمريره مكان الباراميتر .obj فيها باراميتر واحد إسمه print_country_info هنا قمنا بتعريف دالة إسمها
# else سيتم إستدعاء الدوال الثلاثة الموجودة فيه. و إن لم يكن كذلك سيتم تنفيذ أمر الطباعة الموضوع في الجملة BaseCountry أصله كائن من كلاس يرث من الكلاس
def print_country_info(obj):
if isinstance(obj, BaseCountry):
obj.name()
obj.capital()
obj.language()
else:
print('You should pass an object of type BaseCountry')
print('----------------------------------------------')
# australia إسمه Australia و كائن من الكلاس egypt إسمه Egypt هنا قمنا بإنشاء كائن من الكلاس
egypt = Egypt()
australia = Australia()
# لكي يتم إستدعاء الدوال الثلاثة منهم australia و egypt و مررنا لها الكائنين print_country_info() هنا قمنا باستدعاء الدالة
print_country_info(egypt)
print_country_info(australia)
# else و تمرير نص عادي لها. لاحظ كيف أنها ستقوم بتنفيذ أمر الطباعة الموضوع في الجملة print_country_info() هنا قمنا باستدعاء الدالة
print_country_info('A string object')
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
Capital: Cairo
Language: Arabic
----------------------------------------------
Country: Australia
Capital: Canberra
Language: English
----------------------------------------------
You should pass an object of type BaseCountry
----------------------------------------------
تطبيق مبدأ تعدد الأشكال مع المصفوفات و مع الوراثة في بايثون
في المثال التالي قمنا بتعريف كلاس مجرّد إسمه BaseCountry يعتبر الكلاس الأساسي لأي كلاس يمثل بلد و بالتالي أي كلاس سننشئه ليمثل بلد ما يجب أن يرث منه. في هذا الكلاس قمنا بتجهيز 3 دوال مجرّدة أيضاً.
بعدها قمنا بتعريف كلاس إسمه Egypt و كلاس إسمه Australia يرثان من الكلاس BaseCountry و يفعلان Override لكل الدوال التي ورثوها منه.
في الأخير قمنا بإنشاء كائن من الكلاس Egypt و كائن من الكلاس Australia و من ثم وضعناهما في list إسمه countries.
و بعدها قمنا بإنشاء حلقة تمر على جميع الكائنات الموضوعة في الكائن و تستدعي الدوال الثلالثة من كل كائن تم إنشاؤه من كلاس يرث من الكلاس BaseCountry.
مثال
# يحتوي على 3 دوال فارغة BaseCountry هنا قمنا بإنشاء كلاس إسمه class BaseCountry: def name(self): pass def capital(self): pass def language(self): pass
# حتى نستطيع الوراثة منه BaseCountry الموجود في الموديول BaseCountry هنا قمنا بتضمين الكلاس
from BaseCountry import BaseCountry
# للدوال الثلاثة الموجودة فيه Override و يفعل BaseCountry يرث من الكلاس Egypt هنا قمنا بإنشاء كلاس فارغ إسمه
class Egypt(BaseCountry):
def name(self):
print('Country: Egypt')
def capital(self):
print('Capital: Cairo')
def language(self):
print('Language: Arabic')
# حتى نستطيع الوراثة منه BaseCountry الموجود في الموديول BaseCountry هنا قمنا بتضمين الكلاس
from BaseCountry import BaseCountry
# للدوال الثلاثة الموجودة فيه Override و يفعل BaseCountry يرث من الكلاس Australia هنا قمنا بإنشاء كلاس فارغ إسمه
class Australia(BaseCountry):
def name(self):
print('Country: Australia')
def capital(self):
print('Capital: Canberra')
def language(self):
print('Language: English')
# حتى نستطيع التعامل معهم Australia و Egypt ,BaseCountry هنا قمنا بتضمين الكلاسات
from BaseCountry import BaseCountry
from Egypt import Egypt
from Australia import Australia
# australia إسمه Australia و كائن من الكلاس egypt إسمه Egypt هنا قمنا بإنشاء كائن من الكلاس
egypt = Egypt()
australia = Australia()
# countries قمنا بتسميته list في australia و egypt هنا قمنا بوضع الكائنين
countries = [egypt, australia]
# country و في كل مرة تخزن كائن واحد في الكائن countries هنا قمنا بإنشاء حلقة تمر على كل عنصر ( أي كائن ) في الكائن
# سيتم إستدعاء الدوال الثلاثة الموجودة فيه BaseCountry أصله كائن من كلاس يرث من الكلاس country إذا كان
for country in countries:
if isinstance(country, BaseCountry):
country.name()
country.capital()
country.language()
print('------------------')
•سنحصل على النتيجة التالية عند تشغيل الملف Test.
Capital: Cairo
Language: Arabic
------------------
Country: Australia
Capital: Canberra
Language: English
------------------