GreatBahram
GreatBahram
خواندن ۷ دقیقه·۵ سال پیش

یکبار برای همیشه - Dataclasses

Dataclass is awesome!
Dataclass is awesome!


یکی از ویژگی‌های که توی پایتون ۳.۷ معرفی شد، ماژول dataclasses هست. دیتاکلاس‌ها به وجود اومدن تا جلوی تکرار کد‌های یکسان (boilerplate) رو بگیرن. دیتا‌کلاس جز یک مفهوم‌ بزرگتری به اسمCode Generator هست، که هدف از ساخت Code Generator اینه که یک سری کارهای تکراری و بدون پیچیدگی رو به خود کامپایلر/مفسر واگذار کنیم و وقت‌مون رو بیشتر روی بخش‌های مهم برنامه بذاریم.

پس مزایای Code Generator اینه که:

  • یک کار ساده و تکراری رو برای ما انجام میده.
  • دوم اینکه، برنامه‌ی ما رو ساده می‌کنه (از لحاظ خونایی و تعداد خط کد) و در عین حال باعث افزایش کیفیت برنامه هم میشه.

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

History helps us develop a better understanding of our world (I mean the Python world!)

احتمالا خواننده‌های این مطلب می‌دونن که collections.namedtuple که توی پایتون 2 هم وجود داشته؛ در حقیقت میشه گفت نسل اول code generator در پایتون همین namedtuple بوده. Namedtuple معمولاً اینجوری معرفی میشه که ‌tuple‌ها امکان دسترسی به داده رو فقط از طریق indexing میده (بهمین خاطر بهش می‌گیم Sequence)، اینجوری:

how to use a tuple
how to use a tuple

و این خوانایی کد رو پایین میاره؛ در حالی که با کمک namedtuple ما می‌تونیم از طریق اسم attribute به اون دسترسی داشته باشیم (به عبارت درست‌تر می‌تونیم از dot notation لذت ببریم):

namedtuple is cool
namedtuple is cool

این tupleها و namedtupleها یک سری محدودیت و امکانات دارند، که در پایین راجعبه اونا صحبت می‌کنیم:

  • این tupleها immutable هستند، یعنی شما نمی‌تونید بعد از تعریف‌شون برید مقدار داده‌هاشون رو تغییر بدید. این حرف برای namedtuple هم صدق می‌کنه.

با توجه به مثال بالا، namedtuple خوانایی کد ما رو بالا برده و علاوه بر اون یک سری امکانات دیگه هم به ما داده که شاید در نگاه اولیه دیده نشن.

خیلی از مواقع وقتی قصد مقایسه بین آبجکت‌ها رو داریم، tuple comparison واقعاً گزینه خوبیه چون به صورت پیش‌فرض اینا این ویژگی رو پشتیبانی می‌کنند و کار ما رو راحت می‌کنند.

مثلاً فرض کنید، می‌خواهیم زبان‌های برنامه نویسی رو بر اساس اسم‌شون مرتب کنیم:

What does tuple comparison mean?
What does tuple comparison mean?

همون‌طور که می‌بینید آبجکت‌های ما قابلیت مقایسه با هم دیگه رو دارن. دو تا متدی که قابلیت orderability و Equality رو به ما میدن اینا‌ هستن:

  • __lt__, __gt__, __ge__, __le__ # Order-ability methods
  • __eq__ # Equality or Comparison method

یکی دیگه از ویژگی‌های خوبی دیگه‌ای که tuple‌ها دارند، unpacking هست. مثلا:

Aha! This is unpacking!
Aha! This is unpacking!

این ویژگی این امکان رو میده که خیلی راحت‌ به attribute‌ها یا داده‌های هر آبجکت بدون نیاز indexing دسترسی داشته باشیم (این رفتار توسط متد __iter__ پیاده‌سازی میشه).

و نهایتاً این که namedtuple برای ما یک string representation عالی هم فراهم می‌کنه:

String representation
String representation

پس می‌بینید که namedtuple علی‌رغم تعریف یک خطی‌اش، متد‌های متفاوتی رو برای ما پیاده‌سازی کرده، تا کار رو مارو بسیار راحت کنه. ولی دو تا ایراد اصلی هم این namedtuple داره:

  • اولاً، راه‌ حل آدمیزادی واسه تعیین مقدار اولیه هر attribute وجود نداشت.
  • دوم، اینکه نوع متغیر رو به سختی می‌شد با ایده جدید Type Hints مشخص کرد.

این مشکلات باعث شد که یک namedtuple جدید typing.NamedTuple ساخته بشه. مثال قبلی با سیستم جدید:

Using typing.NamedTuple is not that much bad!
Using typing.NamedTuple is not that much bad!

خب، همون‌طور که می‌بینید استفاده از این آبجکت جدید، نیازمند subclass کردن اون هست و اینکه کنار هر فیلد، نوع اون رو به صورت اختیاری می‌تونیم مشخص کنیم. با کمک typing.NamedTuple دو مشکل اساسی collections.namedtuple تا الان حل شدند، یعنی هم می‌تونیم مقدار پیش‌فرض برای بعضی attribute‌ها مشخص کنیم و هم‌ اینکه از type hints ها لذت ببریم.

یادمون باشه namedtupleها واقعا توی لایه‌های پایینی tuple هستند و بعضاً tuple رفتاری داره که ما دوست نداریم آبجکت ما داشته باشه. به طور مثال، ممکنه مقایسه دو نمونه از کلاس Car معنایی نداشته باشه؟ یا از اون بدتر add operator بین دو نمونه از namedtuple به یک چیزی میرسه که ما نمی‌خواهیم کاربر با آبجکت‌ های ما همچین رفتارهایی بکنه:

Things that you should be cautious about it
Things that you should be cautious about it

بنظر می‌رسه ما هنوزم با اون چیزی که می‌خواهیم خیلی فاصله داریم. چی‌ میشه اگر ما بخواهیم یک آبجکت با کمک اینا بسازیم اما immutable نباشه، اتفاقاً می‌خواهیم تغییرش هم بتونیم بدیم. یا بعضی فیلد‌ها توی مقایسه استفاده بشن، بعضی‌های دیگه استفاده نشن.

معرفی dataclass

دیتا کلاس یه class builder هست که اومده تا چالش‌هایی که اینجا راجعبه‌شون صحبت کردیم رو برای ما حل کنه. به طور کلی میشه گفت که dataclass برای نگهداری داده‌ یا attribute ساخته شده، اما واقعاً هیچ محدودیت فنی برای اضافه کردن method به اون کلاس وجود نداره.

ماژول dataclasses دو تا api اصلی داره:

  • اولی یک دکوریتور هست که اسم‌اش dataclass هست.
  • دومی یک متدِ به اسم field هست برای سفارشی‌سازی attributeهای کلاس تعریف شده.

اول از همه بریم ببینیم کار با این دوست‌ جدیدمون چطوری هست:

This is what I wanted from the beginning!
This is what I wanted from the beginning!

پس برای پیاده‌سازی نیازی به subclass کردن چیزی ندارم و خیلی راحت به صورت decorator ازش استفاده می‌کنیم و پایین‌اش علاوه بر اسم attribute‌ها و کنار اون باید نوع attributeها رو مشخص کنم.

به صورت پیش‌فرض هر کلاسی که ساخته میشه، یک string representation و مقایسه (Equality not order!) رو با خودشون برای ما میارن، اینارو با هم ببنیم:

You get what you said!
You get what you said!

به صورت پیش‌فرض کلاس‌هایی که با dataclass ساخته می‌شن mutable هستند (قابل تغییر هستند):

فرق dataclass با خونواده namedtuple این هست که اینجا شما چندین keyword-argument دارید که با کمک اونا می‌تونید کلاس ساخته شده رو تا حد زیادی تغییر بدید. مثلاً اینجوری می‌تونیم بگیم که کلاس ما immutable باشه:

یا می‌تونیم برای هر فیلدی که خواستیم مقدار اولیه مشخص کنیم:

برای لیست کامل این keyword-argumentها خود مستندات‌اش رو بخونید. همون‌طور که گفتیم ماژول dataclasses یک آبجکت دیگه به اسم field داره، که با کمک اون به صورت خیلی عمیق‌تری می‌تونیم کلاس ساخته شده رو customize کنیم. مثلا ممکن هست بخواهیم یک فیلد توی __repr__ ما نشون داده نشه. یا فلان فیلد خاص توی مقایسه هیچ نقشی نداشته باشه.

به طور کلی میشه این ماژول dataclasses داره متدهایی جادویی رو برای ما تعریف می‌کنه (Magic methods).و این پتانسیل رو داره که باعث افزایش کیفیت کد ما هم بشه.

جمع‌بندی

وقتی با پایتون کار می‌کنیم با کلی آبجکت سروکار داریم که از نظر کیفیت کد در سطح بالایی قرار دارند. هدف ما هم باید این باشه که تا حد زیادی خودمون رو به اون سطح نزدیک کنیم. یکی از ابزارهایی که به ما در رسیدن به این هدف کمک می‌کنه dataclass هست.

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

از اینجا کجا بریم؟
ویدیو

دو تا از ویدیوهای خوبی که من دیدم رو پایین اوردم، یکی‌شون راجعبه تاریخچه و نحوه ساخت و کار کردن dataclass هست؛ ارائه دوم با ایده‌ی اینکه dataclass می‌تونه کد ما رو ساده و تمیزتر کنه ساخته شده.


python
Pythonista, Free Software Enthusiast. GNU/Linux Master. Network Security Researcher. Son. Brother.
شاید از این پست‌ها خوشتان بیاید