~/icsd.ir — bash
SYSTEM_ONLINE

OOP پیشرفته – Inheritance و MRO

در این فصل با مفاهیم پیشرفته شی‌گرایی در پایتون آشنا می‌شویم: وراثت چندگانه (Multiple Inheritance)، MRO (Method Resolution Order)، super()، Mixin classes و الگوهای پیشرفته OOP که در پروژه‌های بزرگ استفاده می‌شوند.

در این فصل با مفاهیم پیشرفته شی‌گرایی در پایتون آشنا می‌شویم: وراثت چندگانه (Multiple Inheritance)، MRO (Method Resolution Order)، super()، Mixin classes و الگوهای پیشرفته OOP که در پروژه‌های بزرگ استفاده می‌شوند.

مرور سریع وراثت ساده

قبل از ورود به مباحث پیشرفته، یک مرور سریع روی وراثت ساده:

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return "صدای حیوان"

class Dog(Animal):
    def speak(self):
        return f"{self.name} می‌گوید: واق!"

dog = Dog("رکس")
print(dog.speak())  # رکس می‌گوید: واق!

super() – کلید وراثت حرفه‌ای

تابع super() به ما اجازه می‌دهد متدهای کلاس والد را فراخوانی کنیم بدون اینکه نام آن را به صراحت بنویسیم:

class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
    
    def info(self):
        return f"{self.brand} {self.model}"

class Car(Vehicle):
    def __init__(self, brand, model, doors):
        super().__init__(brand, model)  # فراخوانی __init__ والد
        self.doors = doors
    
    def info(self):
        # افزودن به اطلاعات والد
        return f"{super().info()} با {self.doors} در"

car = Car("پراید", "111", 4)
print(car.info())  # پراید 111 با 4 در
نکته: همیشه از super().__init__() در ابتدای __init__ کلاس فرزند استفاده کنید تا والد به‌درستی مقداردهی شود.

وراثت چندگانه (Multiple Inheritance)

پایتون وراثت از چند کلاس را پشتیبانی می‌کند:

class Flyable:
    def fly(self):
        return f"{self.name} پرواز می‌کند"

class Swimmable:
    def swim(self):
        return f"{self.name} شنا می‌کند"

class Duck(Flyable, Swimmable):
    def __init__(self, name):
        self.name = name

donald = Duck("دانلد")
print(donald.fly())   # دانلد پرواز می‌کند
print(donald.swim())  # دانلد شنا می‌کند

MRO – Method Resolution Order

وقتی چند کلاس والد وجود دارد، پایتون چطور تصمیم می‌گیرد متد را از کدام یک فراخوانی کند؟ پاسخ: MRO (الگوریتم C3 Linearization).

class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):
    pass

d = D()
print(d.method())     # B (از چپ به راست)
print(D.__mro__)
# (D, B, C, A, object)

print(D.mro())        # شکل لیست
# [D, B, C, A, object]

مثال Diamond Inheritance

یکی از مشکلات کلاسیک وراثت چندگانه، مسئله الماس است:

class Animal:
    def __init__(self):
        print("Animal.__init__")
        super().__init__()

class Mammal(Animal):
    def __init__(self):
        print("Mammal.__init__")
        super().__init__()

class WingedAnimal(Animal):
    def __init__(self):
        print("WingedAnimal.__init__")
        super().__init__()

class Bat(Mammal, WingedAnimal):
    def __init__(self):
        print("Bat.__init__")
        super().__init__()

bat = Bat()
# Bat.__init__
# Mammal.__init__
# WingedAnimal.__init__
# Animal.__init__

print(Bat.__mro__)
# (Bat, Mammal, WingedAnimal, Animal, object)

به لطف MRO، Animal.__init__ فقط یک بار اجرا می‌شود (نه دو بار).

Mixin Classes

Mixin یک کلاس کوچک است که فقط برای ترکیب با کلاس‌های دیگر طراحی شده و خودش به‌تنهایی استفاده نمی‌شود. الگوی بسیار رایج در Django و Flask:

class JSONSerializableMixin:
    """قابلیت تبدیل به JSON اضافه می‌کند"""
    def to_json(self):
        import json
        return json.dumps(self.__dict__, ensure_ascii=False)

class LoggableMixin:
    """قابلیت log اضافه می‌کند"""
    def log(self, message):
        print(f"[{self.__class__.__name__}] {message}")

class TimestampMixin:
    """قابلیت زمان‌گذاری اضافه می‌کند"""
    def __init__(self, *args, **kwargs):
        from datetime import datetime
        self.created_at = datetime.now().isoformat()
        super().__init__(*args, **kwargs)

class User(TimestampMixin, JSONSerializableMixin, LoggableMixin):
    def __init__(self, name, email):
        super().__init__()
        self.name = name
        self.email = email

user = User("علی", "ali@example.com")
user.log("کاربر ساخته شد")
print(user.to_json())
# {"created_at": "2026-05-01T...", "name": "علی", "email": "ali@example.com"}

کلاس‌های انتزاعی (ABC)

ماژول abc برای تعریف کلاس‌های انتزاعی (Abstract Base Classes) استفاده می‌شود:

from abc import ABC, abstractmethod

class Payment(ABC):
    """کلاس انتزاعی - نمی‌توان از آن instance ساخت"""
    
    @abstractmethod
    def pay(self, amount):
        """متد انتزاعی - باید در فرزندان پیاده‌سازی شود"""
        pass
    
    @abstractmethod
    def refund(self, amount):
        pass
    
    def confirm(self):
        """متد عادی که در همه فرزندان مشترک است"""
        return "تراکنش تایید شد"

class ZarinPalPayment(Payment):
    def pay(self, amount):
        return f"پرداخت {amount} از طریق زرین‌پال"
    
    def refund(self, amount):
        return f"بازگشت {amount} از زرین‌پال"

# payment = Payment()  # خطا! نمی‌توان از کلاس abstract instance ساخت
zp = ZarinPalPayment()
print(zp.pay(50000))     # پرداخت 50000 از طریق زرین‌پال
print(zp.confirm())

صفات کلاس vs نمونه

class Counter:
    count = 0  # صفت کلاس - بین همه نمونه‌ها مشترک
    
    def __init__(self, name):
        self.name = name              # صفت نمونه
        Counter.count += 1            # افزایش شمارنده کلاس
    
    @classmethod
    def get_count(cls):
        return cls.count

c1 = Counter("اول")
c2 = Counter("دوم")
c3 = Counter("سوم")

print(Counter.count)        # 3
print(Counter.get_count())  # 3

__slots__ – بهینه‌سازی حافظه

به طور پیش‌فرض، هر شیء پایتون یک __dict__ دارد که صفات را در آن ذخیره می‌کند. با __slots__ می‌توان این را غیرفعال کرد و حافظه کمتری مصرف کرد:

class Point:
    __slots__ = ("x", "y")  # فقط این صفات مجاز
    
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(3, 4)
print(p.x, p.y)  # 3 4

# p.z = 5  # خطا! AttributeError

# مقایسه حافظه
import sys
class Normal:
    def __init__(self, x):
        self.x = x

n = Normal(1)
p = Point(1, 2)
print(sys.getsizeof(n.__dict__))  # ~232 bytes
# p اصلاً __dict__ ندارد

@dataclass – کلاس‌های داده‌محور

دکوراتور dataclass از پایتون 3.7 برای ساخت سریع کلاس‌های داده‌ای:

from dataclasses import dataclass, field
from typing import List

@dataclass
class Product:
    name: str
    price: float
    tags: List[str] = field(default_factory=list)
    discount: float = 0.0
    
    def final_price(self):
        return self.price * (1 - self.discount)

p = Product("فرش دستباف", 5_000_000, tags=["کاشان", "پشمی"], discount=0.1)
print(p)
# Product(name='فرش دستباف', price=5000000, tags=['کاشان', 'پشمی'], discount=0.1)
print(p.final_price())  # 4500000.0

frozen dataclass – شیء غیرقابل تغییر

@dataclass(frozen=True)
class Coordinate:
    lat: float
    lng: float

c = Coordinate(35.6892, 51.3890)
# c.lat = 36  # خطا! FrozenInstanceError

# قابل استفاده در set و dict (hashable است)
points = {Coordinate(1, 2), Coordinate(3, 4)}

بهترین شیوه‌ها

  • همیشه از super().__init__() در کلاس‌های فرزند استفاده کنید
  • ترتیب کلاس‌های والد اهمیت دارد (چپ به راست در MRO)
  • برای ترکیب قابلیت‌ها، Mixin بهتر از وراثت چندگانه عمیق است
  • برای کلاس‌های ساده داده‌ای، از @dataclass استفاده کنید
  • اگر کلاس داده‌ای زیاد می‌سازید، __slots__ حافظه را کاهش می‌دهد
  • کلاس‌های انتزاعی (ABC) برای تعریف interface استفاده کنید

جمع‌بندی

  • وراثت چندگانه قابلیت ترکیب چند کلاس را می‌دهد
  • MRO ترتیب جستجوی متدها در زنجیره وراثت است
  • super() برای فراخوانی متدهای والد بدون نام صریح
  • Mixin برای افزودن قابلیت‌های کوچک قابل استفاده مجدد
  • @dataclass و __slots__ برای کدهای تمیزتر و سریع‌تر

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.