یکی از ویژگیهای که توی پایتون ۳.۷ معرفی شد، ماژول 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)، اینجوری:
و این خوانایی کد رو پایین میاره؛ در حالی که با کمک namedtuple ما میتونیم از طریق اسم attribute به اون دسترسی داشته باشیم (به عبارت درستتر میتونیم از dot notation لذت ببریم):
این tupleها و namedtupleها یک سری محدودیت و امکانات دارند، که در پایین راجعبه اونا صحبت میکنیم:
با توجه به مثال بالا، namedtuple خوانایی کد ما رو بالا برده و علاوه بر اون یک سری امکانات دیگه هم به ما داده که شاید در نگاه اولیه دیده نشن.
خیلی از مواقع وقتی قصد مقایسه بین آبجکتها رو داریم، tuple comparison واقعاً گزینه خوبیه چون به صورت پیشفرض اینا این ویژگی رو پشتیبانی میکنند و کار ما رو راحت میکنند.
مثلاً فرض کنید، میخواهیم زبانهای برنامه نویسی رو بر اساس اسمشون مرتب کنیم:
همونطور که میبینید آبجکتهای ما قابلیت مقایسه با هم دیگه رو دارن. دو تا متدی که قابلیت orderability و Equality رو به ما میدن اینا هستن:
__lt__, __gt__, __ge__, __le__ # Order-ability methods
__eq__ # Equality or Comparison method
یکی دیگه از ویژگیهای خوبی دیگهای که tupleها دارند، unpacking هست. مثلا:
این ویژگی این امکان رو میده که خیلی راحت به attributeها یا دادههای هر آبجکت بدون نیاز indexing دسترسی داشته باشیم (این رفتار توسط متد __iter__ پیادهسازی میشه).
و نهایتاً این که namedtuple برای ما یک string representation عالی هم فراهم میکنه:
پس میبینید که namedtuple علیرغم تعریف یک خطیاش، متدهای متفاوتی رو برای ما پیادهسازی کرده، تا کار رو مارو بسیار راحت کنه. ولی دو تا ایراد اصلی هم این namedtuple داره:
این مشکلات باعث شد که یک namedtuple جدید typing.NamedTuple ساخته بشه. مثال قبلی با سیستم جدید:
خب، همونطور که میبینید استفاده از این آبجکت جدید، نیازمند subclass کردن اون هست و اینکه کنار هر فیلد، نوع اون رو به صورت اختیاری میتونیم مشخص کنیم. با کمک typing.NamedTuple دو مشکل اساسی collections.namedtuple تا الان حل شدند، یعنی هم میتونیم مقدار پیشفرض برای بعضی attributeها مشخص کنیم و هم اینکه از type hints ها لذت ببریم.
یادمون باشه namedtupleها واقعا توی لایههای پایینی tuple هستند و بعضاً tuple رفتاری داره که ما دوست نداریم آبجکت ما داشته باشه. به طور مثال، ممکنه مقایسه دو نمونه از کلاس Car معنایی نداشته باشه؟ یا از اون بدتر add operator بین دو نمونه از namedtuple به یک چیزی میرسه که ما نمیخواهیم کاربر با آبجکت های ما همچین رفتارهایی بکنه:
بنظر میرسه ما هنوزم با اون چیزی که میخواهیم خیلی فاصله داریم. چی میشه اگر ما بخواهیم یک آبجکت با کمک اینا بسازیم اما immutable نباشه، اتفاقاً میخواهیم تغییرش هم بتونیم بدیم. یا بعضی فیلدها توی مقایسه استفاده بشن، بعضیهای دیگه استفاده نشن.
دیتا کلاس یه class builder هست که اومده تا چالشهایی که اینجا راجعبهشون صحبت کردیم رو برای ما حل کنه. به طور کلی میشه گفت که dataclass برای نگهداری داده یا attribute ساخته شده، اما واقعاً هیچ محدودیت فنی برای اضافه کردن method به اون کلاس وجود نداره.
ماژول dataclasses دو تا api اصلی داره:
اول از همه بریم ببینیم کار با این دوست جدیدمون چطوری هست:
پس برای پیادهسازی نیازی به subclass کردن چیزی ندارم و خیلی راحت به صورت decorator ازش استفاده میکنیم و پاییناش علاوه بر اسم attributeها و کنار اون باید نوع attributeها رو مشخص کنم.
به صورت پیشفرض هر کلاسی که ساخته میشه، یک string representation و مقایسه (Equality not order!) رو با خودشون برای ما میارن، اینارو با هم ببنیم:
به صورت پیشفرض کلاسهایی که با dataclass ساخته میشن mutable هستند (قابل تغییر هستند):
فرق dataclass با خونواده namedtuple این هست که اینجا شما چندین keyword-argument دارید که با کمک اونا میتونید کلاس ساخته شده رو تا حد زیادی تغییر بدید. مثلاً اینجوری میتونیم بگیم که کلاس ما immutable باشه:
یا میتونیم برای هر فیلدی که خواستیم مقدار اولیه مشخص کنیم:
برای لیست کامل این keyword-argumentها خود مستنداتاش رو بخونید. همونطور که گفتیم ماژول dataclasses یک آبجکت دیگه به اسم field داره، که با کمک اون به صورت خیلی عمیقتری میتونیم کلاس ساخته شده رو customize کنیم. مثلا ممکن هست بخواهیم یک فیلد توی __repr__ ما نشون داده نشه. یا فلان فیلد خاص توی مقایسه هیچ نقشی نداشته باشه.
به طور کلی میشه این ماژول dataclasses داره متدهایی جادویی رو برای ما تعریف میکنه (Magic methods).و این پتانسیل رو داره که باعث افزایش کیفیت کد ما هم بشه.
وقتی با پایتون کار میکنیم با کلی آبجکت سروکار داریم که از نظر کیفیت کد در سطح بالایی قرار دارند. هدف ما هم باید این باشه که تا حد زیادی خودمون رو به اون سطح نزدیک کنیم. یکی از ابزارهایی که به ما در رسیدن به این هدف کمک میکنه dataclass هست.
دیتاکلسها برای کلاسهای سادهای که تعریف میکنیم و پیچیدگی زیادی نداره، خیلی گزینه خوبی هست. اما من خودم وقتی برای کارهای پیچیده ازش استفاده کردم، دیدم بهترین گزینه نبوده.
دو تا از ویدیوهای خوبی که من دیدم رو پایین اوردم، یکیشون راجعبه تاریخچه و نحوه ساخت و کار کردن dataclass هست؛ ارائه دوم با ایدهی اینکه dataclass میتونه کد ما رو ساده و تمیزتر کنه ساخته شده.