البرمجة الموجهة للكائنات OOP في Python - جزء3 الوراثة Inheritance

البرمجة الموجهة للكائنات OOP في Python - جزء3 الوراثة Inheritance

تعتبر الوراثة (Inheritance) أحد المفاهيم الأساسية في البرمجة الموجهة للكائنات (OOP)...حيث تسمح الوراثة Inheritance بإعادة استخدام الـ Classes الموجودة وتوسيعها إذا لزم الأمرعوضاً عن تصميم Classes جديدة... فكيف يمكن تنفيذ الوراثة في لغة البرمجة Python ..!! وكيف تحقق الوراثة Inheritance مفاهيم التجريد Abstraction وإعادة الكتابة Overriding…؟؟!!


الوراثة (Inheritance) من أحد المفاهيم الأساسية في البرمجة الموجهة للكائنات (OOP)، التي تُساهم في إعادة استخدام الكود وتقليل التكرار، كما تُساهم الوراثة في تنظيم الكود بشكل أفضل.. في هذا المقال نتعرف على طريقة تنفيذ الوراثة Inheritance في لغة البرمجة بايثون كما نتعرف على بعض مفاهيم الـ OOP المرتبطة بالوراثة مثل التجريد (Abstraction) واعادة الكتابة (Overriding) ، لكن قبل البدء ننصح بقراءة الجزء 1 & 2 من مقالات البرمجة الموجهة للكائنات OOP  في Python  وهي مقال عن Class & Object و مقال عن التغليف Encapsulation) ، لنبدأ…

في هذا المقال نتعرف على :



الوراثة Inheritance في OOP

تعد الوراثة (Inheritance) أحد المفاهيم الأساسية في لغات البرمجة الموجهة للكائنات (OOP) مثل لغة Python. وتعني الوراثة Inheritance في البرمجة الكائنية (OOP) إنشاء علاقة إشتقاق (توريث) لخصائص ووظائف فئة (Class) إلى أخرى، حيث:

  • يسمى الـ class الذي يتم التوريث منه ( الإشتقاق منه) فئة أساسية (Base Class) أو فئة الوالد Parent Class أو الـ Super class.
  • ويسمى الـ class الذي يرث فئة فرعية (ٍSubclass) أو الفئة المشتقة (Derived Class) أو فئة الابن Child Class.

و يمكن تعريف الوراثة (Inheritance) أنها علاقة يتم بواسطتها انتقال خصائص فئة (Class) ما، إلى فئات (Classes) أخرى مشتقة منها. وتظهر علاقة الوراثة Inheritance عندما تمتلك فئة جديدة علاقة "IS A- هل هو؟" مع فئة Class موجودة. على سبيل المثال:

  • السيارة هي مركبة، والحافلة هي مركبة، هنا المركبة (Vehicle) هي الفئة الأصلية، في حين أن السيارة (Car) والحافلة (Bus)هي الفئات الفرعية.
  • وفي مثال آخر المربع هو شكل و المستطيل هو شكل والمثلث وهو شكل والدائرة هي الأخرى شكل، في هذا المثال يكون الشكل (Shape) هو الـ Base class بينما يكون كلاً من المربع (Square) والمستطيل (Rectangle) و المثلث (Triangle) والدائرة (Circle) جميعها فئات فرعية (Subclass) للفئة الشكل Shape.

وتحقق الوراثة (Inheritance) في البرمجة العديد من الميزات ومنها ما يلي:

  • اتاحة الموارد (Resources Availability) : تمثل الوراثة (Inheritance) علاقات التوريث الواقعية بشكل جيد فجميع المتغيرات والدوال للفئة أساسية (Base class) تكون متاحة لجميع الفئات الفرعية (Subclasses) لهذا الـ Class.
  • تنظيم الكود (Code Organization): يمكن استخدام الوراثة Inheritance لتنظيم الكود بشكل أفضل، حيث يمكن تجزئة الميزات المشتركة بين الـ Classes في فئة أساسية Base class واستخدامها في الفئات المشتقة Derived Classes.
  • إمكانية إعادة استخدام الكود (Code Reusability): لا يتعين علينا كتابة نفس الكود مرارًا وتكرارًا. كما أنها تسمح لنا بإضافة المزيد من الميزات إلى Class دون تعديلها.
  • متعدية (Transitive): الوراثة Inheritance متعدية بطبيعتها، مما يعني أنه إذا ورثت الفئة B من فئة أخرى A، فإن جميع الفئات الفرعية لـ B سترث تلقائيًا من الفئة A.
  • بنية واضحة (Clear Structure) : تقدم الوراثة Inheritance بنية نموذجية بسيطة ومفهومة.
  • توفير نفقات (Save Expenses): يؤدي التوريث إلى تقليل نفقات التطوير والصيانة.

تعتبر الوراثة (Inheritance) أحد المفاهيم الأساسية في البرمجة الموجهة للكائنات (OOP) حيث تُساهم الوراثة Inheritance في إعادة استخدام الكود وتقليل التكرار، كما تُساهم الوراثة في تنظيم الكود بشكل أفضل من خلال تجميع البيانات والوظائف ذات الصلة في Classes معينة. مما يسهل صيانة البرامج من خلال فصل الكود إلى وحدات مستقلة، كما يسهل تحديد الأخطاء وإصلاحها.

الوراثة Inheritance في Python

وعن مفهوم الوراثة (Inheritance) في Python كما هو في البرمجة الكائنية (OOP) ، تتيح لنا الوراثة (Inheritance) في Python إنشاء Class جديد يستفيد من خصائص و وظائف Class موجود بالفعل. بمعنى آخر، يمكن لـ Class جديد (المشتق - Derived Class) أن يرث خصائص و وظائف من Class موجود ( الأساسي - Base class). وتكون الصيغة العامة للوراثة Inheritance في لغة بايثون كتالي :

Class BaseClass:
   {Body}
Class DerivedClass(BaseClass):
   {Body}

كما نرى في الصيغة العامة يتم كتابة اسم الأساسي (Base Class ) بين قوسين عند تعريف الـ Class المشتق (Derived Class) . أما عن تنفيذ الوراثة Inheritance في python فيأخذ عدة أشكال تعتمد على على عدد الـ Classes المشاركين في علاقة التوريث ، ويوضح الشكل التالي أشكال الوراثة Inheritance في بايثون:


الوراثة الفردية Single inheritance:

في الوراثة الفردية (Single inheritance) يكون للفئة المشتقة فئة الابن Child class أن ترث خصائص من فئة أب Parent class واحدة. ويوضح المثال طريقة كيفية تنفيذ الوراثة الفردية Single inheritance حيث قمنا بإنشاء Base class بإسم A و قمنا باشتقاق Subclass باسم B..

...........
# single inheritance
# Base class
class A:
	def func1(self):
		print("This is A class.")

# Derived class
class B(A):
	def func2(self):
		print("This is B class.")

obj0 = B()
obj0.func1()
obj0.func2()
...........

الوراثة الهرمية Hierarchical Inheritance:

نقول عن الوراثة أنها وراثة هرمية (Hierarchical Inheritance).عندما يكون للـ Class أساسي الواحد أكثر من Class مشتق (Derived Class) والأمثلة عديدة على الشكل من الوراثة علاقة الأب و أبناؤه مما يعني أن جميع الـ Classes المشتقة يمكنها الاستفادة من ميزات الـ Class الأساسي . ويوضح المثال التالي طريقة تنفيذ الوراثة الهرمية Hierarchical Inheritance في بايثون:

...........
# Hierarchical inheritance
# Base class 
class A:
	def func1(self):
		print("This is A class.")

# Derived class 1
class B(A):
	def func2(self):
		print("This is B class.")
		
# Derived class 2
class C(A):
	def func3(self):
		print("This is C class.")

obj0 = B()
obj0.func1()
obj1 = C()
obj1.func1()
...........

الوراثة المتعددة Multiple Inheritance:

نقول عن الوارثة أنها من نوع الوراثة المتعددة Multiple Inheritance عندما يمكن اشتقاق Class من أكثر من فئة أساسية (more than one base class)، في الوراثة المتعددة Multiple Inheritance، يتم توريث جميع ميزات الفئات الأساسية إلى الفئة المشتقة. كما يوضح المثال التالي:

...........
# Multiple inheritance
# Base class 1
class A:
	def func1(self):
		print("This is A class.")

# Base class 2
class B:
	def func2(self):
		print("This is B class.")
		
# Derived class
class C (A, B):
	def func3(self):
		print("This is C class.")

obj0 = C()
obj0.func1()
obj0.func2()
obj0.func3()
...........

الوراثة متعددة المستويات Multilevel Inheritance :

في الوراثة متعددة المستويات (Multilevel Inheritance )، يتم توريث ميزات الـ Class (الأساسي - Base class) الـ Class (المشتق - Derived Class) إلى Derived Class مشتق جديدة، وهذا يشبه العلاقة التي بين الجد والأب والحفيد وهو ما يعني أن الـ Class الجديد يمكنه الاستفادة من ميزات الآباء. ويوضح المثال التالي طريقة تنفيذ الوراثة متعددة المستويات Multilevel Inheritance:

...........
# Multilevel inheritance
# Base class 
class A:
	def func1(self):
		print("This is A class.")

# Intermedirctory class
class B(A):
	def func2(self):
		print("This is B class.")
		
# Derived class
class C (B):
	def func3(self):
		print("This is C class.")


obj0 = C()
obj0.func1()
obj0.func2()
obj0.func3()
...........

الوراثة الهجينة Hybrid Inheritance:

نقول عن الوراثة التي تتكون من أشكال متعددة من الوراثة أنها وراثة هجينة (Hybrid Inheritance). ويوضح المثال التالي الوراثة الهجينة حيث تم استخدام نوعين من الوراثة وهي الوارثة الهرمية (Hierarchical Inheritance) والوراثة متعدد المستويات (Multilevel inheritance):


...........
# Hybrid inheritance
# Base class 
class A:
	def func1(self):
		print("This is A class.")

# Hierarchical inheritance, both B and C Classes inherit from A Class
class B(A):
	def func2(self):
		print("This is B class.")
		
class C(A):
	def func3(self):
		print("This is C class.")
		
# Multilevel inheritance, class D inherit from B class which is inherit from A Class
class  D(B):
	def func3(self):
		print("This is C class.")


obj0 = B()
obj0.func1()
obj1 = D()
obj1.func1()
...........


التجريد Abstraction في Python

التجريد (Abstraction) هو أحد المبادئ المهمة في البرمجة الموجهة للكائنات(OOP). يشير التجريد Abstraction إلى نهج برمجي يتم من خلاله عرض البيانات ذات الصلة بالكائن فقط، وإخفاء جميع التفاصيل الأخرى. يساعد هذا النهج في تقليل التعقيد وزيادة كفاءة تطوير التطبيقات.

مثال بسيط على ذلك يمكن أن يكون سيارة. تحتوي السيارة على مسرع وقابض وفرامل ونحن نعلم جميعًا أن الضغط على دواسة الوقود سيزيد من سرعة السيارة وتطبيق الفرامل يمكن أن يوقف السيارة ولكننا لا نعرف الآلية الداخلية للسيارة وكيف يمكن أن تعمل هذه الوظائف يُعرف إخفاء التفاصيل هذا باسم تجريد البيانات.

في بايثون هناك نوعان من التجريد Abstraction:

  • -الأول هو تجريد البيانات (Data Abstraction): حيث يتم إخفاء كيان البيانات الأصلي عبر بنية بيانات يمكنها العمل داخليًا من خلال كيانات البيانات المخفية.
  • -النوع الآخر يسمى تجريد العملية (Process Abstraction): يشير إلى إخفاء تفاصيل التنفيذ الأساسية لعملية ما.

التجريد Abstraction و الوراثة Inheritance في بايثون

يعمل مبدأ التجريد على إخفاء تفاصيل التنفيذ المعقدة بينما يعرض فقط المعلومات والوظائف الأساسية للمستخدمين. في بايثون، يمكننا تحقيق تجريد البيانات من خلال الوراثة (Inheritance) حيث تعمل الوارثة على توسيع عمل الوظائف الموجودة في الـ Subclasses عن مثيلتها في الـ Base Class.

ويتحقق التجريد Abstraction بواسطة الوراثة Inheritance عن طريق الفئات المجردة (Abstract classes) والدوال المجردة (Abstract Method) باستخدام وحدة "abc" (الفئة الأساسية المجردة - Abstract Base Class) .

الفئة المجردة (Abstract class): هو الـ Class الذي يتم فيه تعريف دالة أو أكثر على أنها دالة مجردة Abstract Method. يتم إنشاء الفئات المجردة (Abstract classes) باستخدام وحدة abc (الفئة الأساسية المجردة - Abstract Base Class).

الدالة المجردة (Abstract Method): عندما يتم إعلان دالة داخل الـ Class دون تنفيذها، تُعرف بالدالة المجردة (Abstract Method). في بايثون، لا تعد ميزة الدالة المجردة ميزة افتراضية. بحيث يتعين علينا لإنشاء دالة مجردة Abstract method أو فئات مجردة Abstract classes استيراد للمكتبة "ABC" و"abstractmethod" من مكتبة abc (الفئة الأساسية المجردة).

و تجبر الدالة المجردة (Abstract Method) للـ Base Class فئتها الفرعية على كتابة تنفيذ جميع الدوال المحددة كدوال مجردة في الـ CLass الأساسي بحيث إذا لم يكتب تنفيذ لدوال الـ Class الأساسي المجردة في الـ Subclass سيحدث خطأ للكود.


في المثال التالي طريقة تنفيذ التجريد Abstraction في Python سنقوم بعمل وارثة هرمية Hierarchical Inheritance للـ Abstract class المسمى "A":

...........
# Python Abstraction
# Import required modules
from abc import ABC, abstractmethod
# Crate abstract class as a base class, and it is must inhert from ABC  
class A(ABC):
  # Create abstract method      
  @abstractmethod
  def func(self):
    pass

# Derived class 1
class B(A):
	def func(self):
		print("This is B class.")
		
# Derived class 2
class C(A):
	def func(self):
		print("This is C class.")

obj0 = B()
obj0.func()
obj1 = C()
obj1.func()
...........

نلاحظ في المثال أن الدالة المجردة في الـ Base Class تم إنشاؤها باستخدام @abstractmethod ، كما نلاحظ الدوال الموجودة في الـ Subclasses لها نفس اسم الدالة المجردة في الـ Base Class وهو ما يجب أن يكون. أن عملية كتابة الكود للدوال في الـ Subclasses تسمى بإعادة الكتابة Overriding.



إعادة الكتابة Overriding في Python

في الوراثة (inheritance) قد نرغب أحياناً في الحصول على وظائف خاصة أو مختلفة في الفئة الفرعية (ٍSubclass) تشابه تلك الموجودة في الـ Class الأساسي، يأتي مفهوم إعادة الكتابة (Overriding) للإشارة الى التوسع في وظيفة موجودة بالفعل.

يشير إعادة الكتابة Overriding في بايثون إلى إعادة الكتابة الدالة في الـ Class الحالي الى مثليتها في الـ Class الأساسي له ، حيث تُعريف الدالة في الـ Subclass بنفس اسم دالة في الـ Class الأساسي. في هذه الحالة، يحدد مُفسِّر بايثون الدالة التي سيتم استدعاؤها في وقت التشغيل استنادًا إلى الـ Object الذي تم انشاءه.

في Python يختلف الـ Overriding عن التجريد Abstraction، أنه لا يتطلب استيراد لمكتبات خاصة، كما أن الدالة في الـ Base Class يمكن أن تحتوي على كود قابل للتنفيذ، كذلك الدالة في Subclass يمكن أن تنفذ تعليماتها الخاصة في حال عدم وجود تعليمات خاصة بالـ Overriding.

ويتم تنفيذ الـ Overriding في بايثون من خلال الاشارة الى الدالة في الـ Class الاساسي ويتم هذا بطريقتين:

  • الدالة Super() والوراثة
  • اسم الفئة الأساسية Base Class.

اسم الفئة الأساسية Base Class

في هذه الطريقة ببساطة داخل الدالة الخاصة بالـ subclass نستدعي دالة الـ class الأساسي مع كتابة اسم الـ Class الأساسي (المورث) قبل اسم الدالة، كما يوضح المثال التالي:

...........
#Overriding - Class name 
# Base class
class A:
	def func(self):
		print("This is A class.")

# Derived class
class B(A):
	def func(self):
	  #Overriding for "func()" method in a base class using class name
	  A.func(self)
	  print("This is B class.")

obj0 = B()
obj0.func()
...........

تعتبر هذه الطريقة هي الأفضل للتطبيق مفهوم الـ Overriding في الوارثة فمع اشكال التوريث المختلفة مثل الوراثة المتعددة و الوراثة المتعددة المستويات والوراثة الهجينة يعطي اسم الـ class دقة في تحديد نسخة الدالة المراد التوسع او اعادة الكتابة لها.كما ان هذه الطريقة تعمل بشكل جيد مع الدوال المدمجة للـ Classes مثل __int__ دالة الـ Constructor ودالة إرجاع النص __Str__ ، لاحظ التطبيق في المثال التالي:

...........
#Overriding - Class name 
# Base class 
class A:
	def __str__(self):
		return"This is A class."

# Intermedirctory class
class B(A):
	def __str__(self):
		return"This is B class."
		
# Derived class
class C (B):
	def __str__(self):
	  
	  return A.__str__(self) + '\nThis is C class.'


obj0 = C()
print(obj0)
...........

الدالة Super()

في المجمل تستخدم دالة super() مع الوراثة inheritance للإ شارة إلى عناصر الـ Base Class ، في بايثون هي دالة مدمجة تسمح بوصول الفئة الفرعية (Subclasses) إلى وظائف وخصائص الـ Base Class. كما يوضح المثال التالي.

في المثال التالي احتوى كل من الـ class الأساسي والفرعي على دالة بنفس الاسم func() وعندما اردنا استدعاء الدالة الخاصة بالـ Class الأساسي ، قمنا باستخدام super()

...........
#Overriding super function
# Base class
class A:
	def func(self):
		print("This is A class.")

# Derived class
class B(A):
	def func(self):
	  #Overriding for "func()" method in a base class using super function
	  super().func()
	  print("This is B class.")
		

obj0 = B()
obj0.func()
...........

يعتبر الـ Overriding من المفاهيم القوية في OOP تُتيح للمبرمجين تعديل عمل الـ Class المُشتقة دون التأثير على عمل الـ Class الأساسية، مما يُساهم في إنشاء برامج قابلة للتوسع والصيانة بسهولة. كما تُساهم إعادة الكتابة (Overriding) في تحسين قابلية قراءة الكود وصيانته.

جميع الأمثلة لهذا المقال متوفرة على الرابط هنـا.


إن الوراثة (Inheritance) من مبادئ الـ OOP القوية، فهي تُساهم في تحقيق مبدأ تعدد الأشكال (Polymorphism) أحد مبادئ الـ OOP أيضاً، حيث تُتيح للمبرمجين إنشاء Classes جديدة من Classes موجودة باستخدام خصائصها ووظائفها ، ومن خلال التجريد (Abstraction) وإعادة الكتابة(Overriding) نرى طُرق متعددة للتنفيذ وظيفة واحدة، وهو ما يُساهم في إنشاء برامج أكثر تنظيمًا وكفاءة.

إرسال تعليق

فضلاً اترك تعليق

أحدث أقدم

نموذج الاتصال