Dataclass در پایتون چیست؟

آموزش ساخت کلاس‌های تمیز و حرفه‌ای با @dataclass

Dataclass چیست؟

در پایتون از کلاس‌ها برای ساخت اشیایی استفاده می‌کنیم که می‌توانند داده و رفتار داشته باشند.

اما گاهی یک کلاس فقط برای نگهداری اطلاعات استفاده می‌شود و بخش زیادی از کد آن مربوط به تعریف اولیه، نمایش شیء، مقایسه اشیا و مدیریت مقادیر پیش‌فرض است.

Dataclass قابلیتی در پایتون است که ایجاد چنین کلاس‌هایی را ساده‌تر می‌کند.

با استفاده از دکوراتور @dataclass پایتون می‌تواند بعضی از متدهای رایج کلاس را به‌صورت خودکار ایجاد کند.

مشکل کلاس معمولی چیست؟

فرض کنید می‌خواهیم اطلاعات یک کاربر را نگهداری کنیم.

در یک کلاس معمولی ممکن است کدی شبیه این بنویسیم:

class User:

    def __init__(self, name, age):
        self.name = name
        self.age = age


user = User("Ali", 25)

print(user.name)
print(user.age)

این کد کاملاً درست است، اما برای کلاس‌هایی که عمدتاً وظیفه نگهداری داده دارند، می‌توان آن را ساده‌تر کرد.

ساخت اولین Dataclass

برای استفاده از Dataclass ابتدا باید آن را از ماژول dataclasses وارد کنیم.

from dataclasses import dataclass


@dataclass
class User:

    name: str
    age: int

حالا می‌توانیم یک شیء از این کلاس ایجاد کنیم:

user = User("Ali", 25)

print(user)

خروجی می‌تواند به شکل زیر باشد:

User(name='Ali', age=25)

Dataclass بسیاری از کارهای تکراری را برای ما انجام می‌دهد.

Dataclass و __init__

یکی از مزایای مهم Dataclass این است که بر اساس فیلدهای تعریف‌شده می‌تواند متد __init__ را به‌صورت خودکار ایجاد کند.

from dataclasses import dataclass


@dataclass
class Product:

    name: str
    price: float
    quantity: int


product = Product(
    "Laptop",
    1200.0,
    2
)

بنابراین لازم نیست برای این فیلدها به‌صورت دستی __init__ بنویسیم.

Dataclass و Type Hints

Dataclass معمولاً همراه با Type Hint استفاده می‌شود.

@dataclass
class Student:

    name: str
    age: int
    grade: float

Type Hint مشخص می‌کند هر فیلد چه نوع داده‌ای را انتظار دارد و ساختار کلاس را خواناتر می‌کند.

برای آشنایی بیشتر با Type Hints می‌توانید درس قبلی را نیز مطالعه کنید.

مقدار پیش‌فرض در Dataclass

می‌توان برای فیلدها مقدار پیش‌فرض تعیین کرد.

from dataclasses import dataclass


@dataclass
class User:

    name: str
    age: int = 18


user = User("Ali")

print(user)

چون برای age مقدار پیش‌فرض 18 تعیین شده است، هنگام ساخت شیء لازم نیست مقدار آن را وارد کنیم.

تابع field در Dataclass

برای کنترل دقیق‌تر فیلدها می‌توان از field() استفاده کرد.

from dataclasses import dataclass, field


@dataclass
class Product:

    name: str
    price: float = field(default=0.0)

field() امکانات بیشتری برای تعریف ویژگی‌های فیلد در اختیار ما قرار می‌دهد.

default_factory چیست؟

زمانی که می‌خواهیم مقدار پیش‌فرضی مثل یک لیست یا دیکشنری برای هر شیء به‌صورت جداگانه ساخته شود، می‌توانیم از default_factory استفاده کنیم.

from dataclasses import dataclass, field


@dataclass
class User:

    name: str
    skills: list[str] = field(
        default_factory=list
    )

در این حالت هر شیء یک لیست مستقل برای skills خواهد داشت.

این روش از استفاده اشتباه از یک لیست مشترک جلوگیری می‌کند.

Dataclass و __repr__

Dataclass به‌صورت پیش‌فرض یک نمایش متنی مناسب برای اشیا ایجاد می‌کند.

@dataclass
class User:

    name: str
    age: int


user = User("Sara", 22)

print(user)

خروجی:

User(name='Sara', age=22)

این ویژگی برای Debug کردن و بررسی اطلاعات اشیا بسیار مفید است.

مقایسه اشیا در Dataclass

Dataclass می‌تواند امکان مقایسه بر اساس فیلدهای کلاس را نیز فراهم کند.

from dataclasses import dataclass


@dataclass
class Point:

    x: int
    y: int


p1 = Point(10, 20)
p2 = Point(10, 20)

print(p1 == p2)

خروجی:

True

زیرا مقادیر فیلدهای دو شیء با یکدیگر برابر هستند.

frozen=True چیست؟

اگر بخواهیم پس از ایجاد شیء، مقادیر فیلدهای آن قابل تغییر نباشند، می‌توانیم از frozen=True استفاده کنیم.

from dataclasses import dataclass


@dataclass(frozen=True)
class Point:

    x: int
    y: int


point = Point(10, 20)

حالا تلاش برای تغییر یکی از فیلدها با محدودیت مواجه خواهد شد.

point.x = 50

بنابراین frozen=True برای ساخت اشیایی که نباید پس از ایجاد تغییر کنند، کاربرد دارد.

تبدیل Dataclass به Dictionary با asdict

تابع asdict() می‌تواند یک شیء Dataclass را به Dictionary تبدیل کند.

from dataclasses import dataclass, asdict


@dataclass
class User:

    name: str
    age: int


user = User("Ali", 25)

data = asdict(user)

print(data)

خروجی:

{
    'name': 'Ali',
    'age': 25
}

این قابلیت هنگام تبدیل داده‌ها برای ذخیره‌سازی یا ارسال اطلاعات بسیار کاربردی است.

Dataclass تو در تو

یک Dataclass می‌تواند شامل شیء دیگری از نوع Dataclass باشد.

from dataclasses import dataclass


@dataclass
class Address:

    city: str
    country: str


@dataclass
class User:

    name: str
    address: Address


address = Address(
    "Tehran",
    "Iran"
)

user = User(
    "Ali",
    address
)

print(user)

این روش برای مدل‌سازی داده‌های پیچیده‌تر بسیار مفید است.

آیا Dataclass می‌تواند متد داشته باشد؟

بله. Dataclass فقط برای نگهداری داده نیست و می‌توانیم داخل آن متد نیز تعریف کنیم.

from dataclasses import dataclass


@dataclass
class Product:

    name: str
    price: float
    quantity: int

    def total_price(self) -> float:

        return self.price * self.quantity


product = Product(
    "Mouse",
    20.0,
    3
)

print(product.total_price())

بنابراین Dataclass می‌تواند هم داده و هم رفتار را در خود داشته باشد.

مثال واقعی: مدیریت اطلاعات کاربران

فرض کنید می‌خواهیم اطلاعات کاربران یک برنامه را به شکل مرتب نگهداری کنیم.

from dataclasses import dataclass, field


@dataclass
class User:

    username: str
    age: int
    email: str
    skills: list[str] = field(
        default_factory=list
    )

    def add_skill(self, skill: str) -> None:

        self.skills.append(skill)


user = User(
    "ali_python",
    25,
    "ali@example.com"
)

user.add_skill("Python")
user.add_skill("Django")

print(user)

در این مثال اطلاعات کاربر، لیست مهارت‌ها و متدی برای اضافه کردن مهارت در یک ساختار مشخص قرار گرفته‌اند.

تفاوت Dataclass و Class معمولی

Dataclass در واقع نوع متفاوتی از کلاس نیست؛ بلکه ابزاری است که ساخت کلاس‌های داده‌محور را ساده‌تر می‌کند.

اگر کلاس شما منطق و رفتار پیچیده‌ای دارد، کلاس معمولی همچنان گزینه مناسبی است.

اما اگر هدف اصلی کلاس نگهداری داده‌هاست، Dataclass می‌تواند مقدار زیادی از کدهای تکراری را حذف کند.

کاربردهای Dataclass

  • مدل‌سازی اطلاعات کاربران
  • ساخت مدل‌های داده
  • مدیریت محصولات و سفارش‌ها
  • ذخیره تنظیمات برنامه
  • ساخت ساختارهای داده خوانا
  • مدیریت داده‌های API
  • پروژه‌های وب و Backend
  • پروژه‌های هوش مصنوعی و پردازش داده

اشتباهات رایج در Dataclass

۱. استفاده اشتباه از لیست پیش‌فرض

برای فیلدهایی مانند لیست و دیکشنری بهتر است از default_factory استفاده کنیم.

۲. استفاده از Dataclass برای هر کلاس

Dataclass برای همه کلاس‌ها ضروری نیست. اگر کلاس منطق پیچیده‌ای دارد، یک کلاس معمولی ممکن است مناسب‌تر باشد.

۳. فراموش کردن Type Hint

Dataclass معمولاً با Type Hint استفاده می‌شود و نوشتن نوع فیلدها باعث خوانایی بهتر ساختار داده می‌شود.

جمع‌بندی Dataclass در پایتون

Dataclass یکی از ابزارهای مفید پایتون برای ساخت کلاس‌هایی است که تمرکز اصلی آن‌ها روی نگهداری داده است.

با استفاده از @dataclass بسیاری از کدهای تکراری مانند __init__ و نمایش مناسب شیء به‌صورت خودکار مدیریت می‌شوند.

ابزارهایی مانند field()، default_factory، frozen=True و asdict() نیز امکانات بیشتری در اختیار ما قرار می‌دهند.

ترکیب Dataclass با Type Hints می‌تواند ساختار داده‌های پروژه‌های پایتون را بسیار خواناتر و منظم‌تر کند.

تمرین‌های Dataclass

  1. یک Dataclass به نام Student بسازید که نام، سن و معدل دانش‌آموز را نگهداری کند.
  2. برای یکی از فیلدها مقدار پیش‌فرض تعیین کنید.
  3. یک فیلد لیستی با default_factory ایجاد کنید.
  4. یک متد برای Dataclass بنویسید که اطلاعات شیء را به شکل مناسب نمایش دهد.
  5. یک Dataclass برای محصول بسازید که قیمت و تعداد را دریافت کند و قیمت کل را محاسبه کند.
  6. یک Dataclass با frozen=True بسازید و تفاوت آن را با Dataclass معمولی بررسی کنید.

سوالات متداول درباره Dataclass

Dataclass در پایتون چیست؟

Dataclass ابزاری برای ساخت ساده‌تر کلاس‌هایی است که عمدتاً برای نگهداری داده استفاده می‌شوند.

@dataclass چه کاری انجام می‌دهد؟

این دکوراتور به پایتون اجازه می‌دهد بعضی از متدهای رایج کلاس را بر اساس فیلدهای تعریف‌شده به‌صورت خودکار ایجاد کند.

آیا Dataclass جایگزین کلاس معمولی است؟

خیر. Dataclass یک ابزار برای ساده‌تر کردن برخی کلاس‌هاست. کلاس‌های معمولی همچنان برای بسیاری از کاربردها مناسب هستند.

default_factory چه کاربردی دارد؟

برای ایجاد مقدار پیش‌فرض جدید برای هر شیء استفاده می‌شود؛ مخصوصاً برای ساخت فیلدهایی مانند لیست و دیکشنری.

frozen=True چه کاری انجام می‌دهد؟

باعث می‌شود فیلدهای Dataclass پس از ایجاد شیء به شکل معمول قابل تغییر نباشند.

asdict() چیست؟

asdict() یک شیء Dataclass را به ساختار Dictionary تبدیل می‌کند.