الكلاس في بايثون يجمع بين البيانات والعمليات التي تُجرى عليها
تتيح لك الكلاسات في بايثون تعريف نوع معيّن من الكائنات — مستخدم، حساب بنكي، إعدادات، أو طلب — مع كل ما يمكن لهذا الكائن أن يفعله. هذا "الكائن" هو نسخة (instance) من الكلاس، بينما تُسمّى الأفعال التي يقوم بها ميثودز (methods).
وإليك أبسط شكل ممكن لكلاس:
نفكّكها خطوة بخطوة:
class User:هو بداية تعريف الكلاس. أسماء الكلاسات تُكتب بصيغة PascalCase حسب العُرف المتّبع.__init__هو المُهيِّئ (initializer). يُستدعى تلقائيًا عند إنشاء نسخة (instance) جديدة، ومهمّته ضبط خصائص النسخة عبرself.whatever = ....greetهو ميثود (method)، وselfهو الطريقة التي يُشير بها الميثود إلى النسخة التي استُدعي عليها.User("Rosa", "rosa@example.com")يُنشئ نسخة جديدة، وبايثون تستدعي__init__وتمرّر لها النسخة الجديدة عبرselfمع باقي الوسائط.user.greet()يستدعي الميثود، وبايثون تمرّرuserتلقائيًا كقيمة لـself.
self ليست إلا المُعامل الأول
self ليست كلمة محجوزة في بايثون، بل هي مجرد الاسم المُتعارف عليه للمُعامل الأول في ميثودات النسخة، وهو يُمثّل النسخة التي يعمل عليها الميثود. تقنيًا يمكنك تسميته أيّ شيء، لكن الجميع يستخدم self لأن… الجميع يستخدمه.
كل ميثود نسخة يأخذ self كأول مُعامل له، وبهذا يعرف الميثود على أيّ نسخة سيقرأ أو سيُعدّل بياناتها.
إضافة سلوك للكلاس
الميثودات تستطيع قراءة الحالة وتعديلها:
__repr__ هي دالة خاصة أخرى، ومهمتها الإجابة عن سؤال: "كيف أُعرض هذا الكائن عند طباعته؟". تعريف نسخة جيدة منها يوفّر عليك الكثير من العناء أثناء التنقيح.
الفرق بين class attributes و instance attributes في بايثون
الخصائص التي تُعرَّف داخل جسم الكلاس (خارج __init__) تكون مشتركة بين جميع النسخ (instances)، أما الخصائص التي تُسنَد إلى self فتكون خاصة بكل نسخة على حدة:
استخدم خصائص الكلاس للقيم المشتركة فعليًا — الثوابت والعدّادات وإعدادات التكوين. أما البيانات الخاصة بكل كائن فمكانها داخل __init__.
الوراثة في بايثون
يمكن لأي كلاس أن يرث من كلاس آخر، فيحصل على توابعه وخصائصه جاهزة، ثم يضيف إليها أو يعيد تعريف ما يشاء منها:
تمنحك super() وصولاً إلى الكلاس الأب، وتُستخدم عادةً داخل __init__ لتهيئة خصائص الكلاس الأب قبل إضافة خصائصك الخاصة:
الوراثة أداة مفيدة، لكن من السهل الإفراط في استخدامها. كثيرًا ما يلجأ المبرمج المبتدئ في بايثون إلى الوراثة في حالات يكون فيها التركيب (composition) — أي الاحتفاظ بكائن كخاصية داخل كلاس آخر — أوضح وأبسط. ابدأ ببنية كلاسات مسطّحة، ولا تلجأ إلى الوراثة في بايثون إلا عندما تكون لديك علاقة "هو نوع من" (is-a) حقيقية.
Dataclasses: خيار افتراضي أنظف لتمثيل البيانات
عندما يكون كل ما تريده من الكلاس هو الاحتفاظ ببعض الخصائص مع سلوك مقبول للمقارنة والتمثيل النصي، فإن @dataclass يوفّر عليك الكثير من الكود المتكرر:
قارن هذا بكتابة __init__ و__repr__ و__eq__ يدويًا. الـ dataclasses هي الخيار الأول المناسب في الحالات التي كنت ستكتب فيها class Thing لا يحتوي إلا على __init__ لتعيين الخصائص.
الخصائص المحسوبة باستخدام @property في بايثون
أحيانًا تحتاج إلى "خاصية" تكون في الحقيقة محسوبة. يتيح لك @property أن تجعلها تبدو وكأنها خاصية عادية:
استخدم الـ properties في المواضع اللي كنت ستكتب فيها obj.get_something() — استبدالها بـ property يجعل واجهة الكلاس أكثر طبيعية في الاستخدام. لكن لا تستخدمها لإخفاء عمليات ثقيلة؛ لأن المستخدم يتوقع أن الوصول إلى أي خاصية يكون سريعاً.
الخصوصية بالاصطلاح لا بالإجبار
لا يوجد في بايثون مفهوم private حقيقي. الاصطلاح المتعارف عليه هو أن أي خاصية تبدأ بشرطة سفلية (_) تُعتبر خاصة بشكل ضمني — بمعنى أن القارئ يفهم أنه لا يجب التعامل معها من خارج الكلاس:
class Cache:
def __init__(self):
self._store = {}
def put(self, key, value):
self._store[key] = value
def get(self, key, default=None):
return self._store.get(key, default)
الأسماء التي تبدأ بشرطتين سفليتين (__name) تُفعِّل ما يُعرف بـ name mangling، أي أن بايثون تُضيف اسم الكلاس إلى اسم الخاصية تلقائيًا. هذه الآلية موجودة لتفادي تضارب الأسماء عند الوراثة، وليست وسيلة حماية أو تشفير.
متى لا تحتاج إلى إنشاء كلاس في بايثون؟
إذا كان كل ما لديك مجموعة بيانات مترابطة لا أكثر، فيكفيك dataclass أو قاموس عادي أو namedtuple. وإذا كان لديك دالة واحدة فقط، فلا داعي لتغليفها داخل كلاس. استخدم الكلاسات في بايثون عندما تجتمع لديك الحالة والسلوك معًا — خصوصًا حين تتوقع إنشاء عدد كبير من الكائنات (instances) من النوع نفسه.
مثال أخير
إليك buffer بسيط لتسجيل الأحداث يتصرف كأنه ملف:
لاحظ __len__ — هذه دالة سحرية أخرى (magic method). بمجرد تعريفها يصبح استدعاء len(logger) ممكنًا، لأن len(x) في الحقيقة ليس إلا نداءً لـ x.__len__(). بايثون تحتوي على عشرات من هذه الخطّافات (hooks)، وستتعرف عليها تدريجيًا كلما احتجت إليها.
التالي: المولدات (Generators)
الكلاسات تجمع بين البيانات والسلوك في وحدة واحدة. أما الفصل القادم فسيأخذك إلى نوع مختلف تمامًا من التجريد — وهو التكرار (iteration) — بدءًا من المولدات: دوال تُنتج القيم واحدة تلو الأخرى، وتتوقف مؤقتًا بين كل قيمة والتي تليها. وهي تنسجم بشكل رائع مع عبارة with ومديري السياق (context managers) الذين ستتعرف عليهم مباشرة بعد ذلك.
الأسئلة الشائعة
ما هو الكلاس (class) في بايثون؟
الكلاس هو قالب أو مخطّط لإنشاء كائنات تجمع البيانات (الخصائص) والسلوك (الميثودز) في مكانٍ واحد. مثلاً class User: يُعرِّف كلاس المستخدم، بينما User(...) يُنشئ نسخة (instance) منه. الكلاسات هي الأداة الأساسية في بايثون لنمذجة أي شيء له حالة وسلوك في آنٍ واحد.
ما معنى self في بايثون؟
self هو البارامتر الأول في أي ميثود تابعة للنسخة (instance method). عندما تستدعي user.greet()، تُمرِّر بايثون الكائن user تلقائياً كـ self. وداخل الميثود يمكنك الوصول إلى خصائص ذلك الكائن عبر self.name مثلاً. الاسم self مجرد اتفاق بين المبرمجين وليس كلمة محجوزة، لكن الكلّ في مجتمع بايثون يلتزم به.
هل بايثون لغة كائنية التوجّه (OOP)؟
نعم، وكل شيء في بايثون هو كائن بما في ذلك الأرقام والنصوص وحتى الدوال. لكن بايثون لا تُجبرك على استخدام البرمجة الكائنية، فتستطيع الاستفادة منها عند الحاجة فقط. كثير من البرامج الواقعية بلغة بايثون تستخدم الكلاسات بحذر، وتعتمد على الدوال العادية وهياكل البيانات البسيطة كلما أمكن ذلك.