الأساليب (Methods) في Python: السلوكيات والوظائف
في الدروس السابقة، تعلمنا عن الخصائص (Attributes) - البيانات التي يحملها الكائن. الآن حان الوقت لتعلم الأساليب (Methods) - الأفعال والسلوكيات التي يمكن للكائن القيام بها. الأساليب هي ببساطة دوال تعيش داخل الصنف وتعمل على بيانات الكائن. إنها ما يجعل الكائنات "حية" وقادرة على التفاعل والقيام بالمهام.
1. ما هي الأساليب (Methods)؟
التعريف الأساسي
الأساليب (Methods) هي دوال تُعرّف داخل الصنف. الفرق الرئيسي بين الدالة العادية والأسلوب:
- الدالة العادية: مستقلة، لا تنتمي لأي شيء، تستقبل بيانات كمعاملات
- الأسلوب (Method): ينتمي لصنف، يعمل على بيانات الكائن، يبدأ دائماً بـ
self
الأساليب تمثل "السلوكيات" أو "الأفعال" التي يمكن للكائن القيام بها:
- كلب يمكنه: النباح، الأكل، النوم، الجري
- حساب بنكي يمكنه: الإيداع، السحب، عرض الرصيد، تحويل الأموال
- سيارة يمكنها: التسارع، الفرملة، تشغيل المحرك، إيقاف المحرك
مثال بسيط جداً
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} ينبح: واف واف!")
# إنشاء كلب
my_dog = Dog("ماكس")
# استدعاء الأسلوب
my_dog.bark()
شرح الكود سطراً بسطر:
- السطر 1: نعرّف صنف Dog
- السطر 2-3: دالة
__init__لتهيئة اسم الكلب - السطر 5: نعرّف أسلوباً اسمه
bark. لاحظ أنه يبدأ بـself - السطر 6: الأسلوب يطبع رسالة تحتوي على اسم الكلب (
self.name) - السطر 9: ننشئ كائن كلب اسمه "ماكس"
- السطر 12: نستدعي الأسلوب
bark. لاحظ أننا لم نمررself- بايثون تفعل ذلك تلقائياً!
2. أساليب تستقبل معاملات
تمرير بيانات إضافية للأساليب
مثل الدوال العادية، يمكن للأساليب استقبال معاملات إضافية (بعد self):
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
def deposit(self, amount):
"""إيداع مبلغ في الحساب"""
self.balance += amount
print(f"تم إيداع {amount} ريال")
print(f"الرصيد الجديد: {self.balance} ريال")
def withdraw(self, amount):
"""سحب مبلغ من الحساب"""
if amount > self.balance:
print(f"رصيد غير كافي! الرصيد الحالي: {self.balance}")
else:
self.balance -= amount
print(f"تم سحب {amount} ريال")
print(f"الرصيد المتبقي: {self.balance} ريال")
# إنشاء حساب
account = BankAccount("أحمد", 1000)
# استخدام الأساليب
account.deposit(500)
print()
account.withdraw(300)
print()
account.withdraw(2000) # محاولة سحب أكثر من الرصيد
شرح الكود سطراً بسطر:
- السطر 1: تعريف صنف BankAccount (حساب بنكي)
- السطر 2-4: دالة
__init__تستقبل اسم المالك والرصيد الأولي - السطر 6: تعريف أسلوب
deposit(إيداع) يستقبلselfوamount(المبلغ) - السطر 8: نضيف المبلغ إلى الرصيد الحالي (
self.balance) - السطر 9-10: نطبع رسائل تأكيد
- السطر 12: تعريف أسلوب
withdraw(سحب) - السطر 14: نتحقق: هل المبلغ المطلوب أكبر من الرصيد؟
- السطر 15: إذا كان كذلك، نطبع رسالة خطأ
- السطر 17: إذا كان الرصيد كافياً، نطرح المبلغ
- السطر 22: ننشئ حساباً برصيد أولي 1000 ريال
- السطر 25: نستدعي
deposit(500)- نمرر المبلغ فقط، بايثون تمررselfتلقائياً - السطر 27: نسحب 300 ريال (سينجح لأن الرصيد كافٍ)
- السطر 29: نحاول سحب 2000 ريال (سيفشل لأن الرصيد غير كافٍ)
3. أساليب تُرجع قيماً
استخدام return في الأساليب
الأساليب يمكنها إرجاع قيم تماماً مثل الدوال العادية:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def get_area(self):
"""حساب المساحة"""
area = self.width * self.height
return area
def get_perimeter(self):
"""حساب المحيط"""
perimeter = 2 * (self.width + self.height)
return perimeter
def is_square(self):
"""التحقق: هل هو مربع؟"""
return self.width == self.height
# إنشاء مستطيل
rect1 = Rectangle(5, 10)
rect2 = Rectangle(7, 7)
# استخدام الأساليب التي تُرجع قيماً
area1 = rect1.get_area()
print(f"مساحة المستطيل 1: {area1}")
perimeter1 = rect1.get_perimeter()
print(f"محيط المستطيل 1: {perimeter1}")
# استخدام القيمة المُرجعة مباشرة
print(f"هل المستطيل 1 مربع؟ {rect1.is_square()}")
print(f"هل المستطيل 2 مربع؟ {rect2.is_square()}")
شرح الكود سطراً بسطر:
- السطر 1: تعريف صنف Rectangle (مستطيل)
- السطر 2-4: تهيئة العرض والارتفاع
- السطر 6-9: أسلوب
get_areaيحسب المساحة (العرض × الارتفاع) ويُرجعها - السطر 11-14: أسلوب
get_perimeterيحسب المحيط ويُرجعه - السطر 16-18: أسلوب
is_squareيتحقق: هل العرض يساوي الارتفاع؟ يُرجع True أو False - السطر 21: ننشئ مستطيلاً عرضه 5 وارتفاعه 10
- السطر 22: ننشئ مستطيلاً عرضه 7 وارتفاعه 7 (مربع!)
- السطر 25: نستدعي
get_area()ونحفظ النتيجة في متغير - السطر 32: نستخدم القيمة المُرجعة مباشرة في
printبدون حفظها
4. أساليب تستدعي أساليب أخرى
التعاون بين الأساليب
الأساليب يمكنها استدعاء أساليب أخرى داخل نفس الصنف باستخدام self:
class Student:
def __init__(self, name):
self.name = name
self.grades = []
def add_grade(self, grade):
"""إضافة درجة"""
self.grades.append(grade)
print(f"تم إضافة درجة {grade} للطالب {self.name}")
def get_average(self):
"""حساب المعدل"""
if not self.grades:
return 0
return sum(self.grades) / len(self.grades)
def get_status(self):
"""تحديد حالة النجاح/الرسوب"""
# استدعاء أسلوب آخر من نفس الصنف
average = self.get_average()
if average >= 50:
return "ناجح"
else:
return "راسب"
def display_report(self):
"""عرض تقرير كامل"""
# استدعاء عدة أساليب
average = self.get_average()
status = self.get_status()
print(f"\n{'='*40}")
print(f"تقرير الطالب: {self.name}")
print(f"{'='*40}")
print(f"الدرجات: {self.grades}")
print(f"المعدل: {average:.2f}")
print(f"الحالة: {status}")
print(f"{'='*40}")
# استخدام الصنف
student = Student("أحمد")
# إضافة درجات
student.add_grade(85)
student.add_grade(90)
student.add_grade(78)
# عرض التقرير (يستدعي أساليب أخرى داخلياً)
student.display_report()
شرح الكود سطراً بسطر:
- السطر 1-4: تعريف الصنف وتهيئة اسم الطالب وقائمة فارغة للدرجات
- السطر 6-9: أسلوب
add_gradeيضيف درجة للقائمة - السطر 11-15: أسلوب
get_averageيحسب المعدل (مجموع الدرجات ÷ عددها) - السطر 17-25: أسلوب
get_statusيحدد النجاح/الرسوب - السطر 20: مهم! نستدعي
self.get_average()- أسلوب يستدعي أسلوباً آخر - السطر 27-39: أسلوب
display_reportيعرض تقريراً كاملاً - السطر 30-31: يستدعي أسلوبين آخرين (
get_averageوget_status) - السطر 42: ننشئ طالباً
- السطر 45-47: نضيف 3 درجات
- السطر 50: نستدعي
display_reportالذي يستدعي الأساليب الأخرى تلقائياً
5. مثال عملي شامل: نظام إدارة مهام
تطبيق متكامل
لنبني نظاماً كاملاً لإدارة المهام يجمع كل ما تعلمناه:
class TaskManager:
"""نظام إدارة المهام"""
def __init__(self, owner):
self.owner = owner
self.tasks = [] # قائمة المهام
self.completed_tasks = [] # المهام المكتملة
def add_task(self, task_name, priority="متوسطة"):
"""إضافة مهمة جديدة"""
task = {
'name': task_name,
'priority': priority,
'completed': False
}
self.tasks.append(task)
print(f"تمت إضافة المهمة: {task_name} (أولوية: {priority})")
def complete_task(self, task_name):
"""تحديد مهمة كمكتملة"""
for task in self.tasks:
if task['name'] == task_name and not task['completed']:
task['completed'] = True
self.completed_tasks.append(task)
print(f"تم إكمال المهمة: {task_name}")
return True
print(f"لم يتم العثور على المهمة: {task_name}")
return False
def get_pending_count(self):
"""عدد المهام المعلقة"""
count = 0
for task in self.tasks:
if not task['completed']:
count += 1
return count
def get_completed_count(self):
"""عدد المهام المكتملة"""
return len(self.completed_tasks)
def get_completion_rate(self):
"""نسبة الإنجاز"""
if not self.tasks:
return 0
completed = self.get_completed_count()
total = len(self.tasks)
return (completed / total) * 100
def display_summary(self):
"""عرض ملخص المهام"""
print(f"\n{'='*50}")
print(f"ملخص مهام: {self.owner}")
print(f"{'='*50}")
print(f"إجمالي المهام: {len(self.tasks)}")
print(f"المهام المكتملة: {self.get_completed_count()}")
print(f"المهام المعلقة: {self.get_pending_count()}")
print(f"نسبة الإنجاز: {self.get_completion_rate():.1f}%")
print(f"{'='*50}")
def display_all_tasks(self):
"""عرض جميع المهام"""
print(f"\n--- قائمة مهام {self.owner} ---")
if not self.tasks:
print("لا توجد مهام")
return
for i, task in enumerate(self.tasks, 1):
status = "✓ مكتملة" if task['completed'] else "○ معلقة"
print(f"{i}. {task['name']} - {task['priority']} - {status}")
# استخدام النظام
print("=== نظام إدارة المهام ===\n")
# إنشاء مدير مهام
manager = TaskManager("أحمد")
# إضافة مهام
manager.add_task("كتابة التقرير الشهري", "عالية")
manager.add_task("مراجعة البريد الإلكتروني", "متوسطة")
manager.add_task("الاتصال بالعميل", "عالية")
manager.add_task("تحديث الموقع", "منخفضة")
# عرض جميع المهام
manager.display_all_tasks()
# إكمال بعض المهام
print("\n--- إكمال المهام ---")
manager.complete_task("مراجعة البريد الإلكتروني")
manager.complete_task("الاتصال بالعميل")
# عرض المهام بعد التحديث
manager.display_all_tasks()
# عرض الملخص
manager.display_summary()
شرح الكود بالتفصيل:
التهيئة (السطور 1-7):
- السطر 1: تعريف صنف TaskManager
- السطر 4: دالة التهيئة تستقبل اسم المالك
- السطر 6: قائمة فارغة لحفظ جميع المهام
- السطر 7: قائمة فارغة للمهام المكتملة فقط
إضافة مهمة (السطور 9-17):
- السطر 9: تعريف أسلوب
add_taskيستقبل اسم المهمة والأولوية (افتراضية: "متوسطة") - السطر 11-15: ننشئ قاموساً يمثل المهمة بثلاث معلومات
- السطر 16: نضيف المهمة للقائمة
إكمال مهمة (السطور 19-28):
- السطر 21: نمر على جميع المهام
- السطر 22: نبحث عن المهمة بالاسم ونتحقق أنها غير مكتملة
- السطر 23: نحدد المهمة كمكتملة
- السطر 24: نضيفها لقائمة المهام المكتملة
الإحصائيات (السطور 30-49):
- السطر 30-36:
get_pending_countيعد المهام غير المكتملة - السطر 38-40:
get_completed_countيعد المهام المكتملة - السطر 42-49:
get_completion_rateيحسب نسبة الإنجاز (المكتملة ÷ الكل × 100)
العرض (السطور 51-72):
- السطر 51-60:
display_summaryيعرض ملخصاً شاملاً ويستدعي أساليب أخرى - السطر 62-72:
display_all_tasksيعرض قائمة مفصلة بكل المهام
6. أخطاء شائعة عند كتابة الأساليب
الخطأ 1: نسيان self
# خطأ
class Calculator:
def add(a, b): # نسينا self!
return a + b
calc = Calculator()
# calc.add(5, 3) # TypeError!
# صحيح
class Calculator:
def add(self, a, b): # يجب إضافة self
return a + b
calc = Calculator()
result = calc.add(5, 3) # يعمل بشكل صحيح
الخطأ 2: نسيان self عند الوصول للخصائص
# خطأ
class Person:
def __init__(self, name):
self.name = name
def greet(self):
# خطأ: name غير معرّف!
print(f"مرحباً {name}") # NameError!
# صحيح
class Person:
def __init__(self, name):
self.name = name
def greet(self):
# يجب استخدام self.name
print(f"مرحباً {self.name}")
الخطأ 3: نسيان self عند استدعاء أسلوب آخر
# خطأ
class Math:
def square(self, x):
return x * x
def sum_of_squares(self, a, b):
# خطأ: يجب استخدام self.square
return square(a) + square(b) # NameError!
# صحيح
class Math:
def square(self, x):
return x * x
def sum_of_squares(self, a, b):
# استخدام self للوصول للأسلوب
return self.square(a) + self.square(b)
ملخص الدرس
- الأساليب (Methods) هي دوال تعيش داخل الصنف وتحدد سلوك الكائن
- كل أسلوب يجب أن يبدأ بمعامل
self - الأساليب يمكنها استقبال معاملات إضافية بعد
self - الأساليب يمكنها إرجاع قيم باستخدام
return - الأساليب يمكنها استدعاء أساليب أخرى باستخدام
self.method_name() - للوصول لخصائص الكائن داخل الأسلوب، استخدم
self.attribute - عند استدعاء أسلوب، لا تمرر
selfيدوياً - بايثون تفعل ذلك تلقائياً
الخطوة التالية
الآن بعد أن فهمنا كيف تعمل الأساليب، لنتعمق في فهم self - هذه الكلمة السحرية التي نستخدمها في كل مكان!