بايثون المتوسط: البرمجة الشيئية في الممارسة - الكود والأوامر التي تهم حقاً

بايثون المتوسط POO: الأساسيات في مقال واحد — كود حقيقي، مخططات وخطوات ملموسة، مقتطفات من دورة مكونة من 36 درساً.

بايثون المتوسط: البرمجة الشيئية في الممارسة - الكود والأوامر التي تهم حقاً

لا نظرية طويلة هنا: نفتح الطرفية ونمارس. إليك أساسيات بايثون المتوسط POO، مستخرجة مباشرة من دورة كاملة تضم 36 درسًا — مع كود حقيقي يمكنك نسخه ولصقه الآن.

tl;dr
  • مقدمة وتثبيت
  • تذكير بايثون
  • دوال متقدمة ولمدا
  • الفئات والكائنات
  • الوراثة وتعدد الأشكال
~$ cat ./parcours.md # Python Intermédiaire POO — 10 فصول
01
مقدمة وتثبيت
→ عرض الدورة→ تثبيت Python و VS Code وبيئة افتراضية+ 1 دروس أخرى
02
مراجعة بايثون
→ المتغيرات والأنواع وهياكل البيانات الأساسية→ الشروط والحلقات وفهم القوائم+ 1 دروس أخرى
03
الدوال المتقدمة واللامدا
→ لامدا، map، filter، reduce→ *args، **kwargs والوسائط الافتراضية+ 1 دروس أخرى
04
الفئات والكائنات
→ لماذا POO؟ الفئات مقابل النسخ→ تعريف فئة: __init__، السمات والطرق+ 2 دروس أخرى
05
الوراثة وتعدد الأشكال
→ وراثة بسيطة→ وراثة متعددة و MRO+ 1 دروس أخرى
06
الطرق الخاصة dunder
→ __str__، __repr__، __len__→ المعاملات المحملة: __add__، __eq__، __lt__+ 1 دروس أخرى
07
الوحدات والحزم
→ الاستيراد، الوحدات ومساحات الأسماء→ إنشاء حزمة خاصة بك مع __init__.py+ 1 دروس أخرى
08
إدارة الأخطاء والاستثناءات
→ try، except، else، finally→ استثناءات مخصصة+ 1 دروس أخرى
🏁
المشروع النهائي (+ 2 فصول في الطريق)
→ ستغادر بمشروع ملموس وقابل للعرض

التغليف، الخصائص والمُعيّنات

NOTEالهدف — تعلم كيفية حماية السمات الداخلية لكائن والتحقق من تعديلها بفضل properties، دون تعقيد الواجهة العامة.

الأهداف التعليمية

TIPفي نهاية هذه الوحدة — ستتمكن من استخدام الاصطلاحات _x و__x، وتحويل سمة إلى property لإضافة التحقق، ومعرفة متى يتم ذلك.

ما هو التغليف؟

يتكون التغليف من إخفاء التفاصيل الداخلية لفئة وعرض واجهة محكومة فقط.

NOTEتشبيه — جهاز تحكم التلفاز: تضغط على "volume +" دون معرفة ما يحدث داخل الدوائر. الواجهة بسيطة والداخل محمي.

اصطلاحات بايثون

بايثون لا يفرض السرية (على عكس جافا). يستخدم اصطلاحات مبنية على الاسم.

الاصطلاحالمعنىالتأثير الفعلي
nomعاموصول حر، جزء من الـAPI
_nomمحمي (اصطلاح)"لا تلمسه إلا إذا كنت تعرف ما تفعله"
__nomخاص (name mangling)يُعاد تسميته إلى _NomClasse__nom
nom_تجنب التصادم مع كلمة مفتاحيةمثال: class_ بدلاً من class
output
class CompteBancaire:
    def __init__(self, titulaire, solde):
        self.titulaire = titulaire        # عام
        self._solde = solde               # اصطلاح: لا تلمسه
        self.__pin = "1234"                # خاص (name mangling)

c = CompteBancaire("Alice", 1000)
print(c.titulaire)                  # OK
print(c._solde)                     # يمكن الوصول إليه لكنه غير مستحسن
# print(c.__pin)                    # AttributeError !
print(c._CompteBancaire__pin)       # يمكن الوصول إليه بالاسم المعدل
WARNINGالـ__ ليس أمانًا — إنه آلية لتجنب التصادمات العرضية في الوراثة. لأمان حقيقي، استخدم أذونات على مستوى النظام.

المشكلة بدون تغليف

output
class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

t = Temperature(25)
t.celsius = -500          # غير منطقي!
print(t.celsius)          # -500

لا يوجد تحقق. القيمة -500 مستحيلة (الصفر المطلق = -273.15) لكن بايثون يقبلها.

@property: تحويل سمة إلى طريقة

يجعل @property استدعاءً مثل obj.attribut ينفذ في الواقع طريقة، دون تغيير صيغة الاستخدام.

output
class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, valeur):
        if valeur < -273.15:
            raise ValueError("تحت الصفر المطلق، مستحيل")
        self._celsius = valeur

t = Temperature(25)
print(t.celsius)            # 25 (يستدعي الـgetter)
t.celsius = 30              # يستدعي الـsetter، OK
t.celsius = -300            # ValueError !
TIPالقوة الخارقة — تحافظ على صيغة t.celsius (البسيطة) لكن تضيف تحكمًا غير مرئي. لا يحتاج كود المستخدم إلى أي تغيير.

Property للقراءة فقط

لسمة محسوبة (مشتقة من غيرها) لا يجب تعديلها مباشرة.

output
class Cercle:
    def __init__(self, rayon):
        self.rayon = rayon

    @property
    def diametre(self):
        return self.rayon * 2

    @property
    def aire(self):
        from math import pi
        return pi * self.rayon ** 2

c = Cercle(5)
print(c.diametre)         # 10
print(c.aire)             # 78.539...
# c.aire = 100            # AttributeError : لا يوجد setter

Property كاملة مع getter وsetter وdeleter

output
class Personne:
    def __init__(self, nom):
        self._nom = nom

    @property
    def nom(self):
        return self._nom

    @nom.setter
    def nom(self, valeur):
        if not isinstance(valeur, str) or len(valeur) < 2:
            raise ValueError("اسم غير صالح")
        self._nom = valeur.title()      # تطبيع

    @nom.deleter
    def nom(self):
        print("حذف الاسم")
        self._nom = None

p = Personne("alice")
print(p.nom)              # Alice (مطبّع)
p.nom = "bob"
print(p.nom)              # Bob
del p.nom                 # حذف الاسم

حالة عملية: CompteBancaire مع التحقق

output
class CompteBancaire:
    def __init__(self, titulaire, solde=0):
        self.titulaire = titulaire
        self._solde = 0
        self.solde = solde         # يمر عبر الـsetter!

    @property
    def solde(self):
        return self._solde

    @solde.setter
    def solde(self, valeur):
        if valeur < 0:
            raise ValueError("رصيد سالب ممنوع")
        self._solde = valeur

    def deposer(self, montant):
        if montant <= 0:
            raise ValueError("يجب أن يكون المبلغ موجبًا")
        self.solde += montant       # يمر عبر الـsetter

    def retirer(self, montant):
        if montant > self._solde:
            raise ValueError("رصيد غير كافٍ")
        self.solde -= montant

c = CompteBancaire("Alice", 1000)
c.deposer(500)
print(c.solde)            # 1500
# c.solde = -100          # ValueError

الوراثة البسيطة

NOTEالهدف — تعلم كيفية إنشاء فئة جديدة انطلاقًا من فئة موجودة، بإعادة استخدام كودها وتكييفه. إنه الآلية الأساسية لتجنب تكرار الكود في POO.

الأهداف التعليمية

TIPفي نهاية هذه الوحدة — ستتمكن من تعريف فئة ابنة ترث من فئة أم، وتجاوز طرقها واستدعاء النسخة الأبوية باستخدام super().

المفهوم

تسمح الوراثة بإنشاء فئة متخصصة انطلاقًا من فئة عامة. تحصل الفئة الابنة على كل ما تقدمه الفئة الأم، ثم تضيف أو تعدل.

NOTEتشبيهالكلب هو حيوان. كل كلب لديه سلوكيات حيوان (أكل، نوم)، بالإضافة إلى سلوكيات خاصة (نباح). نقول إن Chien هو Animal.

المثال الأول

output
class Animal:
    def __init__(self, nom, age):
        self.nom = nom
        self.age = age

    def manger(self):
        print(f"{self.nom} mange")

    def dormir(self):
        print(f"{self.nom} dort")


class Chien(Animal):
    def aboyer(self):
        print(f"{self.nom} : Wouaf !")


rex = Chien("Rex", 5)
rex.manger()              # موروث من Animal
rex.dormir()              # موروث من Animal
rex.aboyer()              # خاص بـChien
print(rex.age)            # موروث

Chien لا يعرف __init__: يستخدم بايثون تلقائيًا ذلك الخاص بـAnimal.

تجاوز (override) طريقة

يمكن للفئة الابنة إعادة تعريف طريقة الأم لتكييف سلوكها.

output
class Animal:
    def parler(self):
        print("Bruit générique d’animal")


class Chien(Animal):
    def parler(self):
        print("Wouaf !")


class Chat(Animal):
    def parler(self):
        print("Miaou")


for a in [Chien(), Chat(), Animal()]:
    a.parler()
# Wouaf !
# Miaou
# Bruit générique d’animal
TIPتعدد الأشكال — نفس الاستدعاء a.parler() ينتج نتيجة مختلفة حسب الفئة الحقيقية لـa. إنه أحد أعمدة POO الأربعة.

super(): استدعاء الطريقة الأبوية

عند تجاوز طريقة، غالبًا ما نريد إكمال السلوك الأبوي بدلًا من استبداله.

output
class Animal:
    def __init__(self, nom, age):
        self.nom = nom
        self.age = age

    def description(self):
        return f"{self.nom}, {self.age} ans"


class Chien(Animal):
    def __init__(self, nom, age, race):
        super().__init__(nom, age)        # يستدعي Animal.__init__
        self.race = race

    def description(self):
        base = super().description()       # يستدعي Animal.description
        return f"{base}, race {self.race}"


rex = Chien("Rex", 5, "Labrador")
print(rex.description())     # Rex, 5 ans, race Labrador
WARNINGالقاعدة — بمجرد أن تمتلك الفئة الابنة __init__ خاصًا بها، يجب استدعاء super().__init__(...) لتهيئة سمات الأم. النسيان = أخطاء مضمونة.

التحقق من القرابة: isinstance وissubclass

output
rex = Chien("Rex", 5, "Labrador")

print(isinstance(rex, Chien))      # True
print(isinstance(rex, Animal))     # True (وراثة)
print(isinstance(rex, Chat))       # False

print(issubclass(Chien, Animal))   # True
print(issubclass(Animal, Chien))   # False
TIPPythonic — استخدم isinstance(x, Animal) بدلًا من type(x) == Animal. الشكل الأول يحترم الوراثة.

متى نستخدم الوراثة؟

العلاقةوراثة؟
Chien هو Animalنعم
Roman هو Livreنعم
Voiture لديه Moteurلا — تركيب
Bibliothèque تحتوي على Livresلا — تركيب
NOTEالقاعدة الذهبية — فضّل التركيب على الوراثة. إذا لم تكن العلاقة تخصصًا حقيقيًا، استخدم سمة بدلًا من الوراثة.

حالة حقيقية: تطبيق مكتبة

output
class Livre:
    def __init__(self, titre, auteur, isbn):
        self.titre = titre
        self.auteur = auteur
        self.isbn = isbn
        self.disponible = True

    def description(self):
        return f"{self.titre} de {self.auteur}"


class Roman(Livre):
    def __init__(self, titre, auteur, isbn, genre):
        super().__init__(titre, auteur, isbn)
        self.genre = genre

    def description(self):
        return f"{super().description()} -- roman {self.genre}"


class LivreNumerique(Livre):
    def __init__(self, titre, auteur, isbn, format_fichier):
        super().__init__(titre, auteur, isbn)
        self.format_fichier = format_fichier

    def telecharger(self):
        print(f"Téléchargement de {self.titre}.{self.format_fichier}")


dune = Roman("Dune", "Herbert", "978...", "science-fiction")
ebook = LivreNumerique("1984", "Orwell", "978...", "epub")

print(dune.description())       # Dune de Herbert -- roman science-fiction
print(ebook.description())       # 1984 de Orwell
ebook.telecharger()              # Téléchargement de 1984.epub

التسلسلات الهرمية الشائعة في بايثون

Lambda، map، filter، reduce

NOTEالهدف — اكتشاف الدوال المجهولة (lambda) والدوال ذات الترتيب الأعلى (map، filter، reduce)، التي تسمح بتحويل وتصفية البيانات بطريقة موجزة جدًا.

الأهداف التعليمية

TIPفي نهاية هذه الوحدة — ستعرف متى تستخدم lambda، وتختار بين map/filter وفهم القائمة حسب السياق.

ما هي lambda؟

اللامدا هي دالة مجهولة (بلا اسم) تتسع في سطر واحد. تستخدم لعمليات قصيرة تُمرر كوسيط لدالة أخرى.

الصيغة

output
lambda parametres: expression

مقارنة

output
def doubler(x):
    return x * 2

doubler_lambda = lambda x: x * 2

print(doubler(5))         # 10
print(doubler_lambda(5))  # 10
NOTEتشبيهdef التقليدية تشبه كتابة وصفة باسم في كتاب. أما lambda فهي كتابة وصفة على ورقة لاصقة تعطيها فورًا لشخص ما.

لامدا متعددة المعاملات

output
somme = lambda a, b: a + b
print(somme(3, 4))                       # 7

est_pair = lambda n: n % 2 == 0
print(est_pair(8))                       # True
WARNINGالحد — اللامدا تحتوي على تعبير واحد فقط. لا يوجد return صريح، ولا عدة أسطر. عند التعقيد استخدم def.

map(): تحويل كل عنصر

map(fonction, iterable) يطبق الدالة على كل عنصر ويعيد مكررًا. غالبًا ما نلفه بـlist().

output
prix = [10, 25, 7, 50]
prix_ttc = list(map(lambda p: p * 1.20, prix))
print(prix_ttc)        # [12.0, 30.0, 8.4, 60.0]

mots = ["python", "poo", "code"]
majuscules = list(map(str.upper, mots))
print(majuscules)      # ['PYTHON', 'POO', 'CODE']

مع عدة تسلسلات

output
a = [1, 2, 3]
b = [10, 20, 30]
sommes = list(map(lambda x, y: x + y, a, b))
print(sommes)          # [11, 22, 33]

filter(): الاحتفاظ بما يجتاز الاختبار

output
notes = [12, 8, 15, 6, 18, 11]
reussies = list(filter(lambda n: n >= 10, notes))
print(reussies)        # [12, 15, 18, 11]

mots = ["chat", "chien", "ours", "lion"]
courts = list(filter(lambda m: len(m) <= 4, mots))
print(courts)          # ['chat', 'ours', 'lion']
va-plus-loin

يغطي هذا المقال المقتطفات الأكثر فائدة — الدورة الكاملة بايثون المتوسط POO (11 فصلاً، 36 درسًا، تمارين مصححة ومشروع نهائي) تأخذك إلى النهاية.

./acceder-au-cours-complet دورة مجانية: إتقان Claude Code

الأسئلة الشائعة

كم من الوقت لتعلم بايثون المتوسط POO؟
مع تقدم منظم (11 فصلاً، 36 درسًا قصيرًا وعمليًا)، يمكن الوصول إلى مستوى تشغيلي في بضعة أسابيع بمعدل 30 إلى 60 دقيقة يوميًا. المهم هو تطبيق كل مفهوم فورًا.
هل هناك متطلبات مسبقة؟
تكفي أساسيات الحاسوب. إذا كنت تعرف استخدام الطرفية وقراءة كود بسيط، فأنت جاهز.
من أين نبدأ عمليًا؟
أعد إنتاج أوامر هذا المقال، ثم تابع الدورة الكاملة بايثون المتوسط POO: تربط الـ36 درسًا بالترتيب، مع تمارين ومشروع نهائي.

📬 هل تريد تلقي هذا النوع من الأدلة كل أسبوع؟ اشترك مجانًا — كود حقيقي، بدون كلام فارغ.