به نام خدا

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

توی خط 19، 20 و 21 همین کار رو کردیم. اما هنوز همه چیز به شکلی حرفه ای درست نیست. همون طور که مشخصه، توی __init__ کلاس descriptor مون (که اینجا NonEmptyString هست) باید اسم ویژگی رو داشته باشیم. اما مشکلی که داریم اینه: داریم اسم ویژگی رو دستی بهش میدیم.
از لحاظ کدنویسی خیلی هم چیز رومخ و زشتیه. فرض کن کلاست 20 تا ویژگی داره که مجبوری برای همه شون از یه کلاس descriptor استفاده کنی. اون وقت مجبوری اسم هر 20 تا ویژگی رو دستی به شیءت بدی.
پس برای حل این مشکل، اول برمیگردیم به کلاس descriptor مون و متد __init__ ش رو جور دیگه ای مینویسیم:

دیدی چطور __init__ جدید رو چطوری پیاده سازی کردیم؟ همچنان هر شيء descriptor ما یه ویژگی به اسم name داره. ولی دیگه مقدارش رو توی __init__ بهش نمیدیم و فعلا فقط مقدار پیشفرضش رو None میذاریم.
حالا مسئله اصلی اینه: اسم ویژگی رو از کجا بیاریم؟ (منظور اینه که اسم ویژگی ای که رفتار دلخواهمون رو بعدا بهش می چسبونیم. مثل همین first_name، last_name و nickname توی کلاس اصلی مون که Person باشه)
از پایتون 3.6 به بعد یه متد جادویی جدید وارد شد که دقیقا راه حل مشکل ما بود: __set_name__. کجا تعریف میشه؟ توی کلاس descriptor. آیا چیزی برمیگردونه؟ نه. فقط set می کنه (اسمش برازندهشه). پس بریم اول امضاش رو ببینیم:

owner اسم کلاسیه که توش ویژگی ما تعریف شده (همون صاحب descriptor که Person ما هست). برای همین میشه type hint این آرگومان رو type گذاشت. name هم اسم ویژگی ماست و همیشه از نوع str هست. برای مثال وقتی که توی کلاس Person می زنی:

توی متد __set_name__ این نمونه از NonEmptyString، آرگومان owner تو میشه خود Person و nameت هم میشه first_name.
اما کِی فراخوانی میشه؟ کی فراخوانیش می کنه؟ دقیقا تو همون عکس بالا، خط 23 __set_name__ فراخوانی میشه. ما این کارو نمی کنیم. خود مفسر پایتون انجامش میده. برای هر نمونه از descriptor هم فقط یه بار صدا زده میشه. پس بریم بر اساس این توضیحات بدنه تابع رو هم پیاده سازی کنیم:

دیگه لازم نیست به __get__ و __set__ دست بزنیم و همه چیز درسته. پس یک بار کل کد رو ببینیم:

بعد می تونیم براش تست هم بنویسیم.

که خروجیش میشه:

تو قسمت بعدی برای درک بهتر __set_name__ یه کار کوچیک می کنیم👍