Kodokon kodokon.com

البرمجة الكائنية: الأصناف و__init__ والتوابع والوراثة

نظِّم كودك بأصناف مصمَّمة جيدًا: التهيئة، وسمات المثيل أو سمات الصنف، والوراثة ومقايضاتها.

10 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

أنت تتعامل مع كائنات Python طوال الوقت: السلاسل النصية، والقوائم، والملفات. وكتابة أصنافك الخاصة تعني تجميع الحالة (السمات) والسلوك (التوابع) خلف واجهة متماسكة. نقطة مصطلحية كثيرًا ما يُساء فهمها: __init__ ليست بانيًا للكائن. فالكائن موجود سلفًا (أنشأه __new__) عندما تتلقّى __init__ المعامل self؛ ومهمتها هي تهيئة المثيل. أما self فلا شيء سحري فيه: إنه المعامل الأول لكل تابع مثيل، وتمرّره Python تلقائيًا عند الاستدعاء.

PYTHON
class Account:
    def __init__(self, owner, balance=0.0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("amount must be > 0")
        self.balance += amount
        return self.balance

    def __repr__(self):
        return f"Account({self.owner!r}, {self.balance})"

account = Account("Ada")
account.deposit(120.0)
print(account)
صنف كامل: __init__، وتابع أعمال، و__repr__

تمييز حاسم في الواقع العملي: سمات الصنف يُصرَّح بها في جسم الصنف وتتشاركها كل المثيلات؛ أما سمات المثيل فتُنشأ على self، عادةً داخل __init__. وعند القراءة، تبحث Python أولًا في المثيل، ثم ترجع إلى الصنف، ثم إلى الأصناف الأب. والإسناد self.x = ... ينشئ دائمًا سمة مثيل، تحجب سمة الصنف التي تحمل الاسم نفسه. أما المزلق الكلاسيكي في الواقع العملي فهو سمة صنف قابلة للتغيير.

PYTHON
class Basket:
    items = []

    def add(self, item):
        self.items.append(item)

a = Basket()
b = Basket()
a.add("apple")
print(b.items)  # ['apple']: shared list!
مزلق سمة الصنف القابلة للتغيير

تعبّر الوراثة عن علاقة "هو نوع من": فالصنف Car هو نوع من Vehicle. يرث الصنف الفرعي التوابع، ويمكنه إعادة تعريفها، وتتيح له super() استدعاء نسخة الأب - وهي لا غنى عنها داخل __init__ حتى لا تتخطى تهيئة الأب. وتحلّ Python الاستدعاءات باتّباع MRO (ترتيب تحليل التوابع). لا تلجأ إلى الوراثة بشكل منهجي: فلإعادة استخدام الكود دون علاقة "هو نوع من"، فضِّل التركيب (كائن يملك كائنًا آخر). والتسلسلات الهرمية العميقة هشّة: إذ يصبح كل صنف أب واجهة برمجية ضمنية لجميع أبنائه.

PYTHON
class Vehicle:
    def __init__(self, brand):
        self.brand = brand

    def describe(self):
        return self.brand

class Car(Vehicle):
    def __init__(self, brand, doors):
        super().__init__(brand)
        self.doors = doors

    def describe(self):
        base = super().describe()
        return f"{base}, {self.doors} doors"

print(Car("Aster", 5).describe())
الوراثة وsuper(): التوسيع دون إعادة الكتابة

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. ما دور __init__ بالضبط؟
    • إنشاء الكائن في الذاكرة
    • تهيئة مثيل أُنشئ سلفًا
    • التصريح بسمات الصنف
    • أن يُستدعى عند كل وصول إلى سمة
  2. يُصرِّح صنف بـ items = [] في جسمه. بعد a.add(1)، التي تنفّذ self.items.append(1)، ماذا يرى مثيل آخر b؟
    • القائمة b.items فارغة
    • القائمة b.items تحتوي على 1: فهي مُشتركة
    • تُطلق Python خطأ AttributeError
  3. داخل Car.__init__، ما فائدة الاستدعاء super().__init__(brand)؟
    • إنشاء مثيل ثانٍ من Vehicle
    • تنفيذ التهيئة المعرَّفة في الصنف الأب
    • نسخ توابع Vehicle إلى Car
    • التحقق من أن Car يرث فعلًا من Vehicle