التغليف والوراثة
التغليف يخفي التفاصيل الداخلية للكائن، ويكشف فقط عن واجهات تشغيل آمنة. الوراثة تسمح لفئة واحدة أن تمتلك تلقائياً وظائف فئة أخرى، متجنبة التكرار. هذان المفهومان هما العمود الفقري للبرمجة كائنية التوجه.
1. التغليف والسمات الخاصة
التغليف يعني أن البيانات الداخلية للكائن لا ينبغي الوصول إليها مباشرة من الخارج — يجب التعامل معها من خلال الطرق.
الشرطة السفلية المفردة _ — اصطلاح للسمات "المحمية"
مثال: الشرطة السفلية المفردة والمزدوجة
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 — يمكن الوصول إليها، لكن غير موصى به
الشرطة السفلية المفردة هي مجرد اصطلاح، غير مفروض — لن تمنعك بايثون من الوصول إليها. إنها مثل الكتابة في التوثيق "هذا داخلي، لا تلمسه."
الشرطة السفلية المزدوجة __ — تشويه الاسم
مثال: فئة الحساب البنكي
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. هذا ليس "خصوصية" حقيقية، فقط يجعل التجاوز العرضي أقل احتمالاً.
private حقيقية؛ بايثون لا تملكها ولا تخطط لإضافتها.
مثال: فئة درجة الحرارة (الصعوبة ⭐⭐)
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
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. الوراثة
الوراثة تتيح للفئة الفرعية أن تمتلك جميع سمات وطرق فئتها الأم:
مثال: وراثة الحيوان
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() لاستدعاء الأب
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
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 تشير إلى أنها للخلط، وليس للاستخدام المستقل. فئة يمكنها خلط عدة Mixins بالإضافة إلى فئة أساسية رئيسية واحدة — أكثر مرونة بكثير من الوراثة الفردية العميقة.
حالات الاستخدام الشائعة
- التحقق من صحة البيانات: استخدم
@property.setterلضمان صحة البيانات عند الإسناد. - سمات للقراءة فقط: استخدم
@propertyبدون setter للسمات للقراءة فقط. - إعادة استخدام الكود: ضع الوظائف المشتركة في الفئة الأساسية؛ الفئات الفرعية تكتب فقط الاختلافات.
- التركيب الوظيفي: استخدم Mixins لإضافة ميزات بمرونة إلى فئات مختلفة.
❓ أسئلة شائعة
@property والطريقة العادية؟ ج:@property يجعل الطريقة تبدو كسمة — لا حاجة لأقواس للوصول، الإسناد يُفعّل setter. إنها أساساً "معترض سمات" — تكتب كوداً كما لو كنت تتلاعب بسمة، لكن طريقة تُنفّذ فعلياً.
س:هل تدعم بايثون الوراثة المتعددة؟ أي مخاطر؟ ج:نعم — class C(A, B). ومع ذلك، الوراثة المتعددة لها "مشكلة الماس" (أي طريقة الأب تُستخدم عندما كلاهما لهما نفس الاسم). تستخدم بايثون MRO (ترتيب حل الطريقة)، بالبحث من اليسار إلى اليمين. Mixins هي نمط تصميم يستفيد من الوراثة المتعددة، مع فئات Mixin صغيرة ومستقلة وظيفياً، متجنبة مشاكل الماس.📖 ملخص
- التغليف:
_تعني "لا تلمس"؛__تُفعّل تشويه الاسم؛@propertyيوفر تحكماً أنيقاً في الوصول - الوراثة: الفئات الفرعية تمتلك تلقائياً سمات وطرق الأب؛
super()تستدعي طرق الأب - تجاوز الطريقة: الفئة الفرعية تعيد تعريف طريقة بنفس اسم الأب
- Mixin: استخدم الوراثة المتعددة لإضافة وظائف بمرونة؛ أفضل من الوراثة العميقة
- بايثون ليس لديها سمات خاصة حقيقية — تعتمد على الاصطلاح وتشويه الاسم
📝 تمارين
-
مبتدئ (الصعوبة ⭐): عرّف فئة
Productمع@propertyلسمةprice— يجب أن تكون موجبة عند التعيين؛ تُرجع القيمة الأصلية عند القراءة. -
متوسط (الصعوبة ⭐⭐): عرّف وراثة من ثلاثة مستويات:
Animal←Mammal←Dog/Cat.Animalلهاnameوage؛Mammalتضيفfur_color؛DogوCatكل منهما تنفذ طريقةspeak()الخاصة بها. -
متقدم (الصعوبة ⭐⭐⭐): أنشئ نظام تسجيل باستخدام
LoggableMixin(يوفر طرقlog_info()،log_error()) وفئة أساسيةApplication(لها سمةname). اجعلWebAppوCLIAppكلتاهما ترث منApplicationوتخلطLoggableMixinلتنفيذ وظيفة التسجيل.



