التغليف والوراثة

التغليف يخفي التفاصيل الداخلية للكائن، ويكشف فقط عن واجهات تشغيل آمنة. الوراثة تسمح لفئة واحدة أن تمتلك تلقائياً وظائف فئة أخرى، متجنبة التكرار. هذان المفهومان هما العمود الفقري للبرمجة كائنية التوجه.


1. التغليف والسمات الخاصة

التغليف يعني أن البيانات الداخلية للكائن لا ينبغي الوصول إليها مباشرة من الخارج — يجب التعامل معها من خلال الطرق.

الشرطة السفلية المفردة _ — اصطلاح للسمات "المحمية"

مثال: الشرطة السفلية المفردة والمزدوجة

PYTHON
class Person:
    def __init__(self, name, age):
        self.name = name
        self._age = age      # شرطة سفلية مفردة: تلميح أنها داخلية

p = Person("Zhang San", 25)
print(p.name)               # Zhang San — سمة عامة
print(p._age)               # 25 — يمكن الوصول إليها، لكن غير موصى به
▶ جرّب الكود

الشرطة السفلية المفردة هي مجرد اصطلاح، غير مفروض — لن تمنعك بايثون من الوصول إليها. إنها مثل الكتابة في التوثيق "هذا داخلي، لا تلمسه."

الشرطة السفلية المزدوجة __ — تشويه الاسم

مثال: فئة الحساب البنكي

PYTHON
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance    # شرطة سفلية مزدوجة: سمة خاصة
    
    def get_balance(self):
        """احصل على الرصيد بأمان"""
        return self.__balance
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False

account = BankAccount("Zhang San", 10000)
print(account.owner)                # Zhang San — عام
# print(account.__balance)          # خطأ! AttributeError
print(account.get_balance())        # 10000 — تم الوصول عبر الطريقة
▶ جرّب الكود

الشرطة السفلية المزدوجة تُفعّل تشويه الاسم__balance يُحوّل داخلياً إلى _BankAccount__balance. هذا ليس "خصوصية" حقيقية، فقط يجعل التجاوز العرضي أقل احتمالاً.

💡 بايثون ليس لديها خصوصية حقيقية. تعتمد على فهم "نحن جميعاً بالغون متفقون" — الشرطة السفلية المفردة تعني "استخدام داخلي"، الشرطة السفلية المزدوجة تمنع تضارب الأسماء. اللغات الأخرى (مثل Java) لديها كلمة private حقيقية؛ بايثون لا تملكها ولا تخطط لإضافتها.

مثال: فئة درجة الحرارة (الصعوبة ⭐⭐)

PYTHON
class Temperature:
    def __init__(self, celsius=0):
        self.__celsius = celsius
    
    def to_fahrenheit(self):
        return self.__celsius * 9 / 5 + 32
    
    def set_celsius(self, value):
        """اضبط درجة الحرارة بأمان — لا يمكن أن تقل عن الصفر المطلق"""
        if value < -273.15:
            print("Temperature cannot be below absolute zero!")
            return
        self.__celsius = value
    
    def get_celsius(self):
        return self.__celsius

t = Temperature(100)
print(f"100°C = {t.to_fahrenheit():.1f}°F")   # 212.0°F

t.set_celsius(-300)      # Temperature cannot be below absolute zero!
t.set_celsius(0)
print(f"0°C = {t.to_fahrenheit():.1f}°F")     # 32.0°F
▶ جرّب الكود

2. @property: التحكم الأنيق في الوصول

كتابة set_celsius() / get_celsius() مطولة. @property يتيح لك التحكم في الوصول مع الحفاظ على صيغة الوصول إلى السمات:

مثال: استخدام @property

PYTHON
class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    @property
    def celsius(self):
        """احصل على درجة الحرارة المئوية"""
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        """اضبط درجة الحرارة المئوية — تحقق تلقائي"""
        if value < -273.15:
            raise ValueError("Temperature cannot be below absolute zero!")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        """فهرنهايت (للقراءة فقط — لا يوجد setter)"""
        return self._celsius * 9 / 5 + 32

t = Temperature(100)
print(t.celsius)                    # 100 — يُتاح كسمة
print(t.fahrenheit)                 # 212.0

t.celsius = 0                       # يُسند كسمة
print(t.celsius)                    # 0

# t.celsius = -300                  # سيُثير ValueError!
# t.fahrenheit = 100                # خطأ! لا يوجد setter
▶ جرّب الكود
💡 مزايا @property: 1) لا حاجة لأقواس للوصول: t.celsius بدلاً من t.get_celsius(). 2) تحقق تلقائي عند الإسناد: t.celsius = value بدلاً من t.set_celsius(value). 3) يمكن تحويل سمة عادية إلى سمة محسوبة دون كسر الكود الموجود.


3. الوراثة

الوراثة تتيح للفئة الفرعية أن تمتلك جميع سمات وطرق فئتها الأم:

مثال: وراثة الحيوان

PYTHON
class Animal:
    """فئة الحيوان الأساسية"""
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return "..."
    
    def eat(self, food):
        return f"{self.name} is eating {food}"

class Dog(Animal):
    """كلب — يرث من Animal"""
    def speak(self):
        return "Woof!"

class Cat(Animal):
    """قطة — ترث من Animal"""
    def speak(self):
        return "Meow!"

dog = Dog("Wangcai")
cat = Cat("Mimi")

print(dog.speak())                  # Woof! (تجاوز طريقة الأب)
print(cat.speak())                  # Meow! (تجاوز طريقة الأب)
print(dog.eat("bone"))              # Wangcai is eating bone (موروث من الأب)
print(cat.eat("fish"))              # Mimi is eating fish (موروث من الأب)
▶ جرّب الكود

super(): استدعاء طرق الأب

مثال: super() لاستدعاء الأب

PYTHON
class Animal:
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)      # استدعِ __init__ الأب
        self.breed = breed          # أضف سمات خاصة بالفرع
    
    def introduce(self):
        return f"I'm {self.name}, a {self.breed}"

dog = Dog("Wangcai", "Golden Retriever")
print(dog.introduce())              # I'm Wangcai, a Golden Retriever
▶ جرّب الكود

4. نمط Mixin

Mixin هو فئة خاصة — لا تُستخدم بشكل مستقل، بل "تُخلط" لإضافة وظائف لفئات أخرى:

مثال: نمط Mixin

PYTHON
class FlyMixin:
    """مزيج قدرة الطيران"""
    def fly(self):
        return f"{self.name} is flying!"

class SwimMixin:
    """مزيج قدرة السباحة"""
    def swim(self):
        return f"{self.name} is swimming!"

class Animal:
    def __init__(self, name):
        self.name = name

class Duck(Animal, SwimMixin, FlyMixin):
    """بطة — يمكنها السباحة والطيران"""
    def speak(self):
        return "Quack!"

class Fish(Animal, SwimMixin):
    """سمكة — يمكنها السباحة فقط"""
    def speak(self):
        return "..."

duck = Duck("Donald")
fish = Fish("Nemo")

print(duck.speak())                 # Quack!
print(duck.swim())                  # Donald is swimming!
print(duck.fly())                   # Donald is flying!
print(fish.swim())                  # Nemo is swimming!
▶ جرّب الكود
💡 اصطلاح تسمية Mixin: أسماء الفئات التي تنتهي بـ Mixin تشير إلى أنها للخلط، وليس للاستخدام المستقل. فئة يمكنها خلط عدة Mixins بالإضافة إلى فئة أساسية رئيسية واحدة — أكثر مرونة بكثير من الوراثة الفردية العميقة.


حالات الاستخدام الشائعة


❓ أسئلة شائعة

س متى يجب استخدام الوراثة مقابل Mixin؟
ج الوراثة مناسبة لعلاقات "هو" — الكلب هو حيوان. Mixins مناسبة لـ "يمتلك قدرة" — البطة يمكنها السباحة. فضّل التركيب (Mixin) على الوراثة، لأن التسلسلات الهرمية العميقة للوراثة صعبة الصيانة. قاعدة جيدة: الوراثة يجب ألا تتجاوز 3 مستويات. س:ما الفرق بين @property والطريقة العادية؟ ج:@property يجعل الطريقة تبدو كسمة — لا حاجة لأقواس للوصول، الإسناد يُفعّل setter. إنها أساساً "معترض سمات" — تكتب كوداً كما لو كنت تتلاعب بسمة، لكن طريقة تُنفّذ فعلياً. س:هل تدعم بايثون الوراثة المتعددة؟ أي مخاطر؟ ج:نعم — class C(A, B). ومع ذلك، الوراثة المتعددة لها "مشكلة الماس" (أي طريقة الأب تُستخدم عندما كلاهما لهما نفس الاسم). تستخدم بايثون MRO (ترتيب حل الطريقة)، بالبحث من اليسار إلى اليمين. Mixins هي نمط تصميم يستفيد من الوراثة المتعددة، مع فئات Mixin صغيرة ومستقلة وظيفياً، متجنبة مشاكل الماس.

📖 ملخص

  • التغليف: _ تعني "لا تلمس"؛ __ تُفعّل تشويه الاسم؛ @property يوفر تحكماً أنيقاً في الوصول
  • الوراثة: الفئات الفرعية تمتلك تلقائياً سمات وطرق الأب؛ super() تستدعي طرق الأب
  • تجاوز الطريقة: الفئة الفرعية تعيد تعريف طريقة بنفس اسم الأب
  • Mixin: استخدم الوراثة المتعددة لإضافة وظائف بمرونة؛ أفضل من الوراثة العميقة
  • بايثون ليس لديها سمات خاصة حقيقية — تعتمد على الاصطلاح وتشويه الاسم

📝 تمارين

  1. مبتدئ (الصعوبة ⭐): عرّف فئة Product مع @property لسمة price — يجب أن تكون موجبة عند التعيين؛ تُرجع القيمة الأصلية عند القراءة.

  2. متوسط (الصعوبة ⭐⭐): عرّف وراثة من ثلاثة مستويات: AnimalMammalDog / Cat. Animal لها name و age؛ Mammal تضيف fur_color؛ Dog و Cat كل منهما تنفذ طريقة speak() الخاصة بها.

  3. متقدم (الصعوبة ⭐⭐⭐): أنشئ نظام تسجيل باستخدام LoggableMixin (يوفر طرق log_info()، log_error()) وفئة أساسية Application (لها سمة name). اجعل WebApp و CLIApp كلتاهما ترث من Application وتخلط LoggableMixin لتنفيذ وظيفة التسجيل.

100%