تخطَّ إلى المحتوى

الكلاسات والكائنات في Python

الدرس 19 من 25 · Python

حتى الآن كنت تحفظ بيانات الطالب في قاموس، وتكتب الدوال التي تتعامل معه في مكان آخر. البرمجة الكائنية (Object-Oriented Programming) تجمع البيانات والدوال التي تخصها في مكان واحد. في هذا الدرس ستتعلم الكلاس (Class) والكائن (Object)، وهما أساس أغلب البرامج الكبيرة.

تخيّل قالب الكعك: القالب نفسه ليس كعكة، لكنك تصنع به كعكات كثيرة، ولكل كعكة طعمها وزينتها. الكلاس هو القالب، والكائنات هي الكعكات.

كلاس Student مثلاً يصف أي طالب: له اسم ودرجة. أما «سارة ودرجتها 95» فهي كائن محدد مصنوع من هذا الكلاس.

class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def introduce(self):
print(f"أنا {self.name} ودرجتي {self.grade}")
sara = Student("سارة", 95)
omar = Student("عمر", 82)
sara.introduce()
omar.introduce()
print(sara.name)
الناتج
أنا سارة ودرجتي 95
أنا عمر ودرجتي 82
سارة

لنفهم الكود جزءاً جزءاً:

  • class Student: تعرّف كلاساً جديداً. جرت العادة أن يبدأ اسم الكلاس بحرف كبير.
  • __init__ دالة خاصة تُستدعى تلقائياً عند إنشاء كل كائن، ومهمتها تجهيز بياناته. قبلها وبعدها شرطتان سفليتان.
  • Student("سارة", 95) ينشئ كائناً جديداً، ويرسل القيمتين إلى __init__.
  • self.name و self.grade هما خصائص (Attributes) الكائن، أي بياناته المحفوظة داخله.
  • introduce دالة داخل الكلاس، وتسمى تابعاً (Method)، ونستدعيها بالنقطة: sara.introduce().

self هي الكائن نفسه الذي نتعامل معه الآن. عندما تكتب sara.introduce() ترسل Python الكائن sara تلقائياً إلى المعامل self، وعندما تكتب omar.introduce() يكون self هو omar. لذلك طبعت الدالة نفسها اسمين مختلفين.

القاعدة: كل تابع داخل الكلاس يبدأ بالمعامل self، وأنت لا ترسله عند الاستدعاء، فـ Python ترسله عنك.

توابع تغيّر حالة الكائن

Section titled “توابع تغيّر حالة الكائن”

قوة الكائنات أنها تحفظ حالتها وتغيّرها بتوابعها. هذا كلاس لحساب بنكي:

class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount > self.balance:
print("الرصيد غير كافٍ")
return
self.balance -= amount
def __str__(self):
return f"حساب {self.owner}: {self.balance} جنيه"
account = BankAccount("ليلى", 100)
account.deposit(50)
account.withdraw(500)
account.withdraw(30)
print(account)
الناتج
الرصيد غير كافٍ
حساب ليلى: 120 جنيه

لاحظ ثلاثة أشياء:

  • المعامل balance=0 له قيمة افتراضية، فيمكن كتابة BankAccount("ليلى") لحساب يبدأ من صفر.
  • التابع withdraw يحمي الرصيد: لا يسمح بسحب أكثر من الموجود. هذه ميزة مهمة للكلاسات، فالقواعد تُكتب مرة واحدة في مكان واحد.
  • __str__ تابع خاص يحدد النص الذي يظهر عند طباعة الكائن بـ print.

خصائص الكلاس وخصائص الكائن

Section titled “خصائص الكلاس وخصائص الكائن”

الخصائص التي نكتبها داخل __init__ بـ self تخص كل كائن وحده. لكن يمكن تعريف خاصية على مستوى الكلاس نفسه، فتكون مشتركة بين كل الكائنات:

class Cat:
legs = 4
def __init__(self, name):
self.name = name
a = Cat("مشمش")
b = Cat("لولو")
print(a.legs, b.legs)
print(a.name, b.name)
الناتج
4 4
مشمش لولو

كل القطط لها أربع أرجل، فوضعنا legs في الكلاس. أما الاسم فيختلف من قطة إلى أخرى، فوضعناه في __init__.

الكائنات قيم عادية: تضعها في قوائم، وتمر عليها بحلقة، وترسلها إلى الدوال:

students = [Student("سارة", 95), Student("عمر", 82), Student("هدى", 67)]
for student in students:
if student.grade >= 80:
print(student.name, "متفوق")
الناتج
سارة متفوق
عمر متفوق

اكتب كلاساً اسمه Book له ثلاث خصائص: العنوان title، وعدد الصفحات pages، والصفحة الحالية current وتبدأ من صفر. أضف تابعاً read(count) يزيد الصفحة الحالية دون أن تتجاوز عدد الصفحات، وتابعاً __str__ يعرض التقدم:

الناتج
كتاب الأيام: قرأت 40 من 180 صفحة
كتاب الأيام: قرأت 180 من 180 صفحة
  • نسيان self في تعريف التابع: إذا كتبت def introduce(): ثم استدعيت sara.introduce() فسترى الخطأ TypeError: Student.introduce() takes 0 positional arguments but 1 was given، لأن Python أرسلت الكائن ولم تجد معاملاً يستقبله.
  • نسيان self. عند استخدام خاصية: داخل التابع، name وحدها متغير عادي غير معرّف. اكتب self.name.
  • كتابة _init_ بشرطة واحدة: لن تعتبرها Python دالة التجهيز. الصحيح شرطتان من كل جهة: __init__.
  • نسيان الأقواس عند الإنشاء: sara = Student لا تنشئ كائناً، بل تجعل sara اسماً آخر للكلاس.

سؤال

متى تُستدعى الدالة __init__؟

سؤال

ما الذي يمثله self داخل التابع؟

  • الكلاس قالب، والكائن نسخة مصنوعة منه لها بياناتها الخاصة.
  • __init__ تجهّز الكائن عند إنشائه، و self تشير إلى الكائن الحالي.
  • الخصائص بيانات الكائن، والتوابع دوال تعمل عليها.
  • __str__ تحدد شكل الكائن عند طباعته.
  • خصائص الكلاس مشتركة بين كل الكائنات، وخصائص self خاصة بكل كائن.