البرمجة كائنية التوجه
نظّم شيفرتك باستخدام الكائنات والأصناف
البرمجة كائنية التوجه في بايثون
البرمجة كائنية التوجه (POO) هي نموذج برمجي ينظّم الشيفرة حول الكائنات بدلًا من الدوال والمنطق. وبما أن بايثون لغة متعددة النماذج، فإنها توفّر دعمًا كاملًا وأنيقًا للبرمجة كائنية التوجه. في هذا الدرس سنستكشف المفاهيم الأساسية لهذا النموذج في بايثون، ونتعلّم كيف نستخدمها بفعالية في برامجك.
لماذا نستخدم البرمجة كائنية التوجه؟
قبل الغوص في التفاصيل، من المهم أن نفهم سبب شعبية هذا النموذج الكبيرة:
- التنظيم: تتيح لك البرمجة كائنية التوجه تنظيم شيفرتك بشكل أكثر بنية وأكثر نمطية (modular).
- إعادة الاستخدام: تسهّل إعادة استخدام الشيفرة بفضل الوراثة والتركيب.
- سهولة الصيانة: الشيفرة المنظّمة جيدًا في شكل كائنات تكون عمومًا أسهل في الصيانة والتطوير.
- التغليف: تتيح البرمجة كائنية التوجه إخفاء التعقيد وحماية البيانات بفضل التغليف.
الأصناف والكائنات
المفهومان الأساسيان في البرمجة كائنية التوجه هما الأصناف (classes) والكائنات (objects).
الأصناف
الصنف هو قالب أو مخطط لإنشاء الكائنات. وهو يحدد السمات (المتغيرات) والطرائق (الدوال) التي ستمتلكها الكائنات.
إليك كيفية تعريف صنف بسيط في بايثون:
class Personne:
def __init__(self, nom, age):
self.nom = nom
self.age = age
def saluer(self):
return f"Bonjour, je m'appelle {self.nom} et j'ai {self.age} ans."
في هذا المثال:
Personneهو اسم الصنف.__init__هي طريقة خاصة (دالة بانية) تقوم بتهيئة كائن جديد.selfيشير إلى نسخة الكائن الحالية.nomوageهما سمتان.saluerهي طريقة.
الكائنات
الكائن هو نسخة (instance) من صنف. لإنشاء كائن، تستدعي الصنف كما تستدعي الدالة:
personne1 = Personne("Alice", 30)
personne2 = Personne("Bob", 25)
print(personne1.saluer()) # Affiche: Bonjour, je m'appelle Alice et j'ai 30 ans.
print(personne2.saluer()) # Affiche: Bonjour, je m'appelle Bob et j'ai 25 ans.
الكائنان personne1 وpersonne2 نسختان مستقلتان من الصنف Personne، ولكل منهما قيم سماته الخاصة.
الطرائق الخاصة
توفّر بايثون عدة طرائق خاصة (تُسمّى أيضًا الطرائق السحرية أو طرائق dunder اختصارًا لـ “double underscore”) تتيح تخصيص سلوك الكائنات.
class Livre:
def __init__(self, titre, auteur, pages):
self.titre = titre
self.auteur = auteur
self.pages = pages
self.page_actuelle = 0
def __str__(self):
"""Définit comment l'objet est représenté sous forme de chaîne."""
return f"{self.titre} par {self.auteur}"
def __len__(self):
"""Définit le comportement quand la fonction len() est appliquée à l'objet."""
return self.pages
def __del__(self):
"""Méthode appelée lorsque l'objet est supprimé."""
print(f"Le livre {self.titre} a été fermé.")
livre = Livre("Python Facile", "J. Dupont", 200)
print(livre) # Utilise __str__
print(len(livre)) # Utilise __len__
del livre # Déclenche __del__
الوراثة
تتيح الوراثة لصنف أن يرث السمات والطرائق من صنف آخر. وهي مفهوم أساسي لتعزيز إعادة استخدام الشيفرة.
class Animal:
def __init__(self, nom):
self.nom = nom
def parler(self):
pass # Méthode à surcharger dans les classes dérivées
class Chien(Animal):
def parler(self):
return f"{self.nom} dit Woof!"
class Chat(Animal):
def parler(self):
return f"{self.nom} dit Meow!"
animaux = [Chien("Rex"), Chat("Misty")]
for animal in animaux:
print(animal.parler())
في هذا المثال:
Animalهو الصنف الأساسي (أو الصنف الأب).ChienوChatصنفان مشتقان (أو صنفان ابنان) يرثان منAnimal.- الطريقة
parlerيُعاد تعريفها (override) في كل صنف مشتق.
الوراثة المتعددة
على عكس بعض لغات البرمجة، تدعم بايثون الوراثة المتعددة، مما يتيح لصنف أن يرث من عدة أصناف أب.
class A:
def methode_a(self):
return "Méthode de A"
class B:
def methode_b(self):
return "Méthode de B"
class C(A, B): # Hérite de A et B
def methode_c(self):
return "Méthode de C"
objet = C()
print(objet.methode_a()) # Hérité de A
print(objet.methode_b()) # Hérité de B
print(objet.methode_c()) # Défini dans C
التغليف
التغليف هو مفهوم تجميع البيانات والطرائق التي تعمل عليها، وتقييد الوصول المباشر إلى متغيرات الكائن.
في بايثون، يتحقق التغليف بالاصطلاح (convention) أكثر منه بالفرض الصارم:
class CompteBancaire:
def __init__(self, solde_initial):
self._solde = solde_initial # Convention: attribut protégé (single underscore)
self.__compte_id = "123456" # Convention: attribut privé (double underscore)
def deposer(self, montant):
self._solde += montant
def retirer(self, montant):
if montant <= self._solde:
self._solde -= montant
return True
return False
def get_solde(self):
return self._solde
compte = CompteBancaire(1000)
compte.deposer(500)
print(compte.get_solde()) # 1500
print(compte._solde) # Accessible, mais par convention, ne devrait pas être accédé directement
# print(compte.__compte_id) # Erreur: renommage par "name mangling"
print(compte._CompteBancaire__compte_id) # Accessible via mangling
في بايثون:
- السمة التي تبدأ بشرطة سفلية واحدة (
_solde) تشير إلى أنها محمية (اصطلاح فقط، ولا يُفرض). - السمة التي تبدأ بشرطتين سفليتين (
__compte_id) تخضع لما يسمى “name mangling” (إعادة التسمية)، مما يجعل الوصول إليها عن طريق الخطأ أصعب، لكنها ليست خاصة فعليًا.
تعدد الأشكال
يتيح تعدد الأشكال (polymorphism) معاملة كائنات من أصناف مختلفة على أنها كائنات من صنف مشترك. وفي بايثون، تعدد الأشكال جزء أصيل من اللغة بفضل الـ duck typing.
def faire_parler(animal):
print(animal.parler())
chien = Chien("Rex")
chat = Chat("Misty")
faire_parler(chien) # Rex dit Woof!
faire_parler(chat) # Misty dit Meow!
تعمل الدالة faire_parler مع أي كائن يملك الطريقة parler، دون الاهتمام بصنفه الفعلي.
الخصائص
تتيح الخصائص (properties) التحكم في الوصول إلى سمات الصنف مع الحفاظ على واجهة نظيفة.
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("La température ne peut pas être inférieure au zéro absolu.")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
self.celsius = (value - 32) * 5/9
temp = Temperature()
temp.celsius = 25
print(f"Celsius: {temp.celsius}°C, Fahrenheit: {temp.fahrenheit}°F")
temp.fahrenheit = 77
print(f"Celsius: {temp.celsius}°C, Fahrenheit: {temp.fahrenheit}°F")
طرائق الصنف وسماته
على عكس طرائق النسخة وسماتها التي تخص كل كائن على حدة، تكون طرائق الصنف وسماته مشتركة بين جميع نسخ الصنف.
class Voiture:
# Attribut de classe
nombre_de_voitures = 0
def __init__(self, marque, modele):
self.marque = marque
self.modele = modele
# Incrémenter l'attribut de classe
Voiture.nombre_de_voitures += 1
@classmethod
def get_nombre_de_voitures(cls):
return cls.nombre_de_voitures
@staticmethod
def est_electrique(marque, modele):
# Une méthode statique qui ne dépend pas de l'état de l'instance
# Liste fictive de voitures électriques
voitures_electriques = [("Tesla", "Model 3"), ("Nissan", "Leaf")]
return (marque, modele) in voitures_electriques
voiture1 = Voiture("Toyota", "Corolla")
voiture2 = Voiture("Honda", "Civic")
print(Voiture.get_nombre_de_voitures()) # 2
print(Voiture.est_electrique("Tesla", "Model 3")) # True
الخاتمة
توفّر البرمجة كائنية التوجه في بايثون طريقة قوية ومرنة لتنظيم شيفرتك. فهي تتيح لك إنشاء برامج أكثر نمطية وقابلية لإعادة الاستخدام وسهولة في الصيانة.
في هذا الدرس، تناولنا المفاهيم الأساسية للبرمجة كائنية التوجه في بايثون، بما فيها الأصناف والكائنات والوراثة وتعدد الأشكال والتغليف. لكن هذه ليست سوى البداية! فهناك العديد من المفاهيم المتقدمة وأنماط التصميم (design patterns) الأخرى التي يمكن أن تساعدك على الاستفادة القصوى من هذا النموذج.
في الدرس القادم، سنستكشف معالجة الاستثناءات في بايثون، وهو جانب حاسم لكتابة برامج متينة.
التعليقات
التعليقات مدعومة عبر GitHub Discussions
سجّل الدخول باستخدام GitHub للمشاركة في النقاش