🧩 برمجة الكائنات (OOP)

🧩 برمجة الكائنات في بايثون (OOP)

تعلم كيف تنظم كودك كالمحترفين باستخدام الكلاسات والكائنات — أقوى مفهوم في البرمجة الحديثة.

🏗️ الكلاسات والكائنات 🔧 الـ __init__ 🧬 الوراثة 🎭 تعدد الأشكال 🔒 التغليف 🚀 مشروع حقيقي

١. لماذا برمجة الكائنات؟

تخيل أنك تبني نظام مدرسة. عندك 200 طالب، كل طالب له اسم وعمر ودرجات. بدون OOP ستكتب 200 مجموعة من المتغيرات. مع OOP تصنع قالباً واحداً (Class) وتنشئ منه 200 طالب في ثواني.

🏗️

Class (الكلاس)

القالب أو المخطط — يصف كيف يبدو الشيء

📦

Object (الكائن)

نسخة حقيقية من الكلاس بقيم فعلية

⚙️

Attribute (الخاصية)

بيانات الكائن — مثل الاسم والعمر

🔧

Method (الدالة)

ما يستطيع الكائن فعله — مثل الدراسة

مثال: كلاس Car كقالب
🏭 Class: Car
القالب
↙     ↓     ↘
🚗 car1
BMW 2023
🚙 car2
Toyota 2022
🚕 car3
Honda 2024

٢. إنشاء أول كلاس

Python — أول كلاس
# تعريف الكلاس
class Car:
    # خاصية مشتركة بين كل الكائنات
    wheels = 4

    def honk(self):
        print("بيب بيب! 📯")

# إنشاء كائنات من الكلاس
car1 = Car()
car2 = Car()

car1.honk()                  # بيب بيب!
print(car1.wheels)           # 4
print(type(car1))             # <class 'Car'>
print(isinstance(car1, Car)) # True
ما هو self؟ self هو إشارة إلى الكائن الحالي. عندما تكتب car1.honk() بايثون يمرر car1 تلقائياً كـ self. دائماً اجعله أول معامل في دوال الكلاس.

٣. دالة __init__ — منشئ الكائن

دالة __init__ تُستدعى تلقائياً عند إنشاء كائن جديد. تستخدمها لتحديد البيانات الأولية للكائن.

Python — __init__
class Student:
    def __init__(self, name, age, city):
        self.name   = name
        self.age    = age
        self.city   = city
        self.grades = []    # قائمة فارغة لكل طالب

    def introduce(self):
        print(f"اسمي {self.name}، عمري {self.age} من {self.city}")

    def add_grade(self, grade):
        self.grades.append(grade)

    def get_average(self):
        if not self.grades:
            return 0
        return sum(self.grades) / len(self.grades)

# إنشاء طلاب
s1 = Student("خليل", 14, "مراكش")
s2 = Student("فاطمة", 15, "الرباط")

s1.introduce()
s1.add_grade(18)
s1.add_grade(16)
print(f"معدل {s1.name}: {s1.get_average()}")  # 17.0

٤. الدوال الخاصة (Dunder Methods)

بايثون لديه دوال مدمجة خاصة بين شرطتين مزدوجتين. أشهرها __str__ لطباعة الكائن بشكل جميل.

Python — __str__ و __repr__
class Book:
    def __init__(self, title, author, pages):
        self.title  = title
        self.author = author
        self.pages  = pages

    def __str__(self):
        # يُستخدم عند print()
        return f"📖 '{self.title}' بقلم {self.author} ({self.pages} صفحة)"

    def __len__(self):
        # يُستخدم مع len()
        return self.pages

    def __eq__(self, other):
        # يُستخدم مع ==
        return self.title == other.title

b1 = Book("ألف ليلة وليلة", "مجهول", 1000)
b2 = Book("المقدمة", "ابن خلدون", 800)

print(b1)        # 📖 'ألف ليلة وليلة' بقلم مجهول (1000 صفحة)
print(len(b1))   # 1000
print(b1 == b2) # False

٥. الوراثة (Inheritance)

الوراثة تسمح لكلاس أن يأخذ كل خصائص ودوال كلاس آخر ويضيف عليها. مثل الإرث الحقيقي!

شجرة الوراثة
👤 Person
name, age, introduce()
↙      ↘
🎓 Student
grades, study()
👨‍🏫 Teacher
subject, teach()
Python — الوراثة
# الكلاس الأب (Parent)
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def introduce(self):
        print(f"مرحباً، اسمي {self.name} وعمري {self.age}")

# الكلاس الابن (Child) يرث من Person
class Student(Person):
    def __init__(self, name, age, school):
        super().__init__(name, age)  # استدعاء init الأب
        self.school = school
        self.grades = []

    def study(self, subject):
        print(f"{self.name} يدرس {subject} 📚")

class Teacher(Person):
    def __init__(self, name, age, subject):
        super().__init__(name, age)
        self.subject = subject

    def teach(self):
        print(f"الأستاذ {self.name} يدرّس {self.subject} 👨‍🏫")

# استخدام
student = Student("خليل", 14, "ثانوية مراكش")
teacher = Teacher("الأستاذ محمد", 40, "الرياضيات")

student.introduce()  # موروثة من Person
student.study("بايثون")
teacher.introduce()
teacher.teach()

٦. super() — التواصل مع الأب

super() تتيح لك استدعاء دوال الكلاس الأب. مفيد جداً لإضافة سلوك على ما موجود بدل إعادة كتابته.

Python — super()
class Animal:
    def __init__(self, name):
        self.name = name
        print(f"🐾 تم إنشاء حيوان: {name}")

    def eat(self):
        print(f"{self.name} يأكل 🍖")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)    # init الأب
        self.breed = breed

    def bark(self):
        print(f"{self.name} ينبح: هاو هاو! 🐕")

    def eat(self):
        super().eat()               # سلوك الأب أولاً
        print("  ثم يهز ذيله 🐾")  # ثم نضيف سلوكاً جديداً

rex = Dog("ريكس", "Labrador")
rex.bark()
rex.eat()

٧. تعدد الأشكال (Polymorphism)

نفس اسم الدالة يتصرف بشكل مختلف حسب الكائن. كلهم يعرفون "يتكلمون" لكن كل واحد بطريقته.

Python — Polymorphism
class Animal:
    def speak(self):
        return "صوت..."

class Dog(Animal):
    def speak(self):
        return "هاو هاو! 🐕"

class Cat(Animal):
    def speak(self):
        return "مياو! 🐱"

class Cow(Animal):
    def speak(self):
        return "خوار! 🐄"

# نفس الدالة، نتائج مختلفة
animals = [Dog(), Cat(), Cow(), Dog()]
for animal in animals:
    print(f"{type(animal).__name__}: {animal.speak()}")

٨. التغليف (Encapsulation)

التغليف يخفي البيانات الداخلية ويمنع تعديلها مباشرة. تستخدم _ أو __ قبل اسم الخاصية.

Python — Encapsulation
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner    = owner
        self.__balance = balance  # خاص — __ يمنع الوصول المباشر

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"✅ تم إيداع {amount} درهم")

    def withdraw(self, amount):
        if amount > self.__balance:
            print("❌ رصيدك غير كافٍ!")
        else:
            self.__balance -= amount
            print(f"💸 تم سحب {amount} درهم")

    def get_balance(self):
        return f"💰 رصيد {self.owner}: {self.__balance} درهم"

acc = BankAccount("خليل", 500)
acc.deposit(200)
acc.withdraw(100)
print(acc.get_balance())       # 600 درهم
# print(acc.__balance)  ← خطأ! لا يمكن الوصول
متى تستخدم التغليف؟ استخدم __ للبيانات الحساسة (كلمات مرور، أرصدة) التي لا يجب أن يعدلها أحد مباشرة.

🚀 المشروع — نظام إدارة مكتبة

طبّق OOP في مشروع حقيقي يجمع كل المفاهيم: كلاس الكتاب، كلاس المكتبة، الاستعارة والإرجاع.

Python — library_system.py
# ============================
# 📚 نظام إدارة مكتبة
# ============================

class Book:
    def __init__(self, title, author, year):
        self.title       = title
        self.author      = author
        self.year        = year
        self.is_borrowed = False

    def __str__(self):
        status = "❌ مستعار" if self.is_borrowed else "✅ متاح"
        return f"📖 {self.title} ({self.author}, {self.year}) — {status}"

class Library:
    def __init__(self, name):
        self.name  = name
        self.books = []

    def add_book(self, book):
        self.books.append(book)
        print(f"✅ أُضيف: {book.title}")

    def show_all(self):
        print(f"\n📚 مكتبة {self.name} — {len(self.books)} كتاب:")
        for i, b in enumerate(self.books, 1):
            print(f"  {i}. {b}")

    def borrow(self, title, person):
        for book in self.books:
            if book.title == title:
                if book.is_borrowed:
                    print(f"❌ '{title}' مستعار حالياً")
                else:
                    book.is_borrowed = True
                    print(f"📗 {person} استعار '{title}'")
                return
        print(f"❓ الكتاب '{title}' غير موجود")

    def return_book(self, title):
        for book in self.books:
            if book.title == title:
                book.is_borrowed = False
                print(f"✅ تم إرجاع '{title}'")
                return

# ---- اختبار النظام ----
lib = Library("مكتبة مراكش")

lib.add_book(Book("ألف ليلة وليلة", "مجهول", 800))
lib.add_book(Book("المقدمة", "ابن خلدون", 1377))
lib.add_book(Book("كليلة ودمنة", "ابن المقفع", 750))

lib.show_all()
lib.borrow("المقدمة", "خليل")
lib.borrow("المقدمة", "أحمد")  # مستعار
lib.return_book("المقدمة")
lib.show_all()

🎉 أتقنت برمجة الكائنات!

الآن تعرف الكلاسات، __init__، الوراثة، super()، تعدد الأشكال، والتغليف.

الدرس التالي: تحليل البيانات ببايثون — ستتعلم pandas وmatplotlib.

الدرس التالي: تحليل البيانات ←