Software Engineering for Data Scientists: Object Oriented Programming and Function Programming
خب. OOP و FP دو استایل برنامهنویسی هستند که یک جورایی نشون میدن که کد و منطق حل مساله رو چطور باید به قطعات کوچکتری شکست. اهمیت آشنایی با اونها در اینه که شما حتی اگر مثلا روی یکی از این استایلها کد نزده باشید هم احتمالا با پکیجهای پایتونی مواجه میشید که متدهاشون رو به صورت انحصاری از یکی از این استایلها استفاده کرده، پس لازمه که حتما یک درکی از OOP و FP داشته باشیم.
استایل Object Oriented
پایه این استایل object هست. اگر بخوایم فلسفیتر نگاه کنیم حالا object چیه؟ object هر چیزیه که بشه با یک اسم توصیفش کرد. مثلا دیتافریم یک آبجکته، آرایه نامپای یک آبجکته، فیگر matplotlib یک آبجکته، و حتی در scikit هم اون چیزی که estimate میکنه و شما روش متد fit میزنید، آبجکته. چون همه اینها یک چیزیاند که امکان نامگذاری رو میدن.
به علاوه اون خاصیت نامگذاری، آبجکت میتونه دیتا نگهداری کنه. مثلا یک دیتافریم شامل تعدادی ستون در خودش میتونه باشه.
پس کلا object در نگاه فلسفی oop یعنی شما چیزی دارید که هم میتونید با نام توصیفش کنید و هم براتون دیتا نگهداری میکنه.
در کنار object حالا سه تا مفهوم دیگه class و method و attribute هم قرار میگیرن که نیاز به توضیح نیست. class داره نحوه آبجکت رو تعریف میکنه، متد میگه شما رو این آبجکت چه کارها میتونید بکنید و attribute هم که یک جورهایی داره دیتای داخل آبجکت رو مشخص میکنه.
اینجا کتاب یک مثال جالب درباره matplotlib میزنه. که در اینجا قابل مشاهده است:
https://ryxcommar.com/2020/04/11/why-you-hate-matplotlib/
داستان اینه که احتمال شما هم تجربه گیج شدن در کشیدن نمودار در matplotlib رو داشتید. matplotlib در واقع دو جور api در اختیار شما قرار میده، یک سری به فرم object oriented هست و دیگریشون به فرم شبه functional هست. در واقع وقتی در سال ۲۰۰۳ منتشر شده، چون میخواسته جوری باشه که برای کسایی که MATLAB کار میکنن هم آسون باشه، به این فرم هم ریلیز کرده و در طول زمان دیگه چون ملت به هر دو فرم ازش استفاده میکردند نتونسته یک فرم رو حذف کنه و اکنون شما با دو ملغمه از apiها در matplotlib گاها گیج میشید.
در ادامه کتاب شروع کرده به زدن مثال و نوشتن کد با روش oop. یک کار جالبی که کرده و توجه من رو به خودش جلب کرده، اینه که برای توضیح OOP عوض این که بیاد و مثل مثالهای رایج که مثلا میگن خونه رو پیادهسازی کن، اومده یک فانکشن رو سعی کرده با oop پیاده کنه تا اتفاقا از ذهن دانشجو بتونه کلیشه زدایی کنه و لب مطلب oop رو بهتر بهش بفهونه. مثلا مثالی که زده اینه که گفته من میخوام یک متنی رو چند بار پشت سر هم تکرار کنم این رو چطور میشه oop زدش؟ خب قاعدتا شما اگر ذهنتون فانکشنال باشه با یک فانکشن ردیفش میکنید ولی این اومده oop طور این طور پیاده کرده:
class RepeatText():
def __init__(self, n_repeats):
self.n_repeats = n_repeats
def multiply_text(self, some_text):
prin((some_text + " ")* self.n_repeats)
در توضیح مفاهیم فلسفی، ارزشها و اخلاقیات هم باید همینجوری به نظرم رفتار کرد. یعنی وقتی میخوایم core value یک چیز رو نشون بدیم سعی کنیم اتفاقا بیایم و با مثالهای غیررایج پیادهسازیش کنیم.
در ادامه یک توضیحی هم راجع به class methods و static methods میده. class method متدی هست که روی کل یک کلاس اعمال میشه (نه روی یک نمونه از اون کلاس) و static method هم متدی هست که میشه بدون این که لازم باشه instace ای از یک کلاس ساخته بشه،فراخوانی بشه. در واقع در اینها عوض این که مثلا بزنیم object.folan فراخوانی به شکل Class.folan صورت میگیره.
در ادامه کتاب اومده راجع به ترمهای تخصصیتری که در OOP هست صحبت کرده.
اولیش inheritence هست که نیاز به توضیح نداره. شما وقتی میخواید یک کلاسی بسازید که انگار extend شده یک کلاس دیگه است، منطقی نیست که اون کلاس جدید رو بیاید از نو همه چیش رو دوباره بزنید (یاد اصول ماژولاریتی بیافتید) پس میتونید صرفا اون رو روی اون کلاسی که ازش extend شده سوارش کنید.
بعدش سراغ Encapsulation میره. این یعنی این که کلاس شما باید جزییات درون خودش رو (مثل اتریبیوتها) از بیرونیها مخفی نگه داره و راه برقراری با این اطلاعات داخل هر آبجکت، باید استفاده از interface و متدهای واسط باشه. در پایتون Encapsulation مثل جاوا رایج نیست ولی مثلا همین الان در مثلا padnas dataframeها شما مستقیما به خود منطق ذخیرهسازی داده دسترسی ندارید و صرفا با یک سری interface با خود اون دادهها در ارتباطید.
بعد سراغ Polymorphism میره. پرسش Polymorphism چیه؟ میگه اگر مثلا من چند تا کلاس مختلف داشتم و اینها در عین حال تفاوت، یک کار یکسانی براشون وجود داشت که هر کدومشون میتونست انجام بده چطور این باید پیاده بشه؟ مثالش خوبش میشه مثل مورد زیر اتفاقی که برای مدلهای scikit میافته:
from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier lr_clf = LogisticRegression() lr_clf.fit(X_train, y_train) rf_clf = RandomForestClassifier() rf_clf.fit(X_train, y_train)
در اینجا شما ممکنه مدلهای مختلفی داشته باشید ولی چون همشون متد fit رو با یک نام دارند خیلی راحت صرفا با تغییر کلاس میتونید اون متد fit رو برای همشون اجرا بگیرید. در واقع اتفاقی که در Polymorphism در پایتون میافته، به اشتراکگذاری نام متدهاست.
استایل Functional Programming
در فانشکنال پروگرمینگ ما فلسفه مون اینه که فانکشن نباید هیچ دیتایی که خارج از اون فانکشن هست رو تغییر بده. در واقع ما اصلا چیزی به عنوان آبجکت که بتونیم روش اسم بگذاریم و اون هم دادهای برامون ذخیره کنه نخواهیم داشت و به این میگیم نداشتن side effect. حالا این تفسیر و دید خودش طیف مختلفی میتونه داشته باشه. در FP وحشی و خالص، یک برنامه صرفا و فقط از یکسری evaluating function تشکیل شده و حتی به جای این که آبجکتی رو ورودی بگیره یا پس بدن،صرفا فانکشن به عنوان آرگومان ورودی یا خروجی میپذیرند.
حالا مزیت همیچن قیود و شروط عجیبی چیه؟
- تستکردن برنامه خیلی راحت میشه. چون دیگه هر فانکشنی همیشه یک خروجی یکسان برای یک ورودی میفرسته بیرون و فانکشن هم قول میده که دادهای رو تغییر نده که شرایطی پیش بیاد که تستهامون با ورودی-خروجی یکسان الزما یکسان نباشند. (یعنی ممکنه در شرایطی که شما side effect دارید ورودی و خروجیهای یک فانکشتون یکسان باشه ولی چون فانکشنه داره یک دیتایی رو انگولک میکنه رفتارهای برنامهتون الزاما یکسان نباشه)
- به خاطر این که دیتا دستکاری نمیشه، راحتتر میشه موازیسازی کرد. میتونید با خیال راحت هر شاخه از برنامه رو بسپرید دست یکی.
- مجبور میشید که کد ماژولار بنویسید.
- وقتی صرفا فانکشن دارید و مطمئنید دیتایی هم اون وسط تغییر نمیکنه، انگار وقتی برنامهتون رو مینویسید، گراف برنامهتون رو رسم کردید و میشه قبل از اجرا، گراف رو بهینه هم کرد.
متدها و کانسپتهایی از پایتون که مخصوص نوع نگاه Functional هستند اینها هستند:
lambda functions, map, filter
به علاوه مفاهیم زیر هم اغلب تو همین کانسپت به کار میرن:
generators, list comprehension, itertools
در ادامه کتاب راجع به lambda functionها و map صحبت کرده. لامبدا فانکشن، فانکشنی هست که فقط و فقط یک اکسپرشن میتونه داشته باشه و به همین خاطر مصداق بارز نه به ساید افکت هست! یعنی فرض کنید اگر شما برنامهتون رو فقط با لامبدا فانکشن بزنید میتونید مطمئن باشید که هیچ ساید افکتی نداره. map و filter هم یک فانکشن میگیرن و اون رو به iterable اعمال میکنن.
در ادامه هم کتاب یک مقدار راجع به pandas و numpy و خاصیتهای FP که در اینها هست صحبت کرده.