آشنایی سریع با جزییات اشیای پایتون – به زبان ساده
آیا تاکنون پیش آمده که یک شیء پایتون داشته باشید که بخواهید به سرعت با جزییات آن آشنا شوید؟ شاید هم پیش آمده که شیء آشنایی داشتهاید و به دنبال یک متد خاص میگشتهاید، اما نمیدانستهاید آن را چگونه برای گوگل توصیف کنید. زمانی که در زمینه علوم داده روی اشیای پیچیده در کتابخانههایی مانند TensorFlow کار میکنید، بارها با این موقعیت مواجه خواهید شد. در چنین مواردی آرزو میکنید که روش سریعتری برای شناخت جزییات اشیای پایتون در کتابخانههای جدید وجود داشته باشد، چون ممکن است مستندات در دسترس نباشد یا نادرست باشند و یا بررسی آنها زمانبر باشد.
در این مقاله به روش بررسی عمیق اشیا پایتون میپردازیم و یک ابزار CLI قابل نصب از طریق pip معرفی میکنیم که pip dis (+) نام دارد و همه این کارها را برای شما انجام میدهد. اگر میخواهید مستقیماً با این ابزار آشنا شوید میتوانید این بخش را رد کرده و یکراست به سراغ بخش «ابزار بازبینی شیء CLI به نام Peep Dis» بروید.
بررسی شیء
در این بخش به عنوان یک مثال ساده یک کلاس Rectangle یا چند متد و خصوصیت تعریف میکنیم:
تابع dir یک تابع داخلی ساده است که همه خصوصیتها و متدهای یک شیء را به جز __dir__ که overload شده است، فهرستبندی میکند.
این همان خصوصیت است که ادیتورها و IDE-ها برای «تکمیل خودکار» (Auto-complete) مورد استفاده قرار میدهند.
>>> rect = Rectangle(3., 4.) >>> dir(rect) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'area', 'b', 'bisect', 'scale']
خروجی ما لیستی از رشتهها است که خصوصیتها و متدهای شیء را نمایش میدهد که غالباً شامل موارد داخلی هستند. به طور معمول موارد درونی چندان مفید نیستند و فقط باعث افزایش شلوغی میشوند.
فیلتر کردن موارد داخلی
بسته به تعریف موارد درونی میتوان یا از فیلترینگ رشته یا فیلترینگ نوع برای حذف آنها استفاده کرد.
فیلترینگ رشته
فیلترینگ نوع
فیلترینگ رشته، همه متدها و خصوصیتهای جادویی را حذف میکند، در حالی که فیلترینگ به وسیله BuiltinMethodType متدهای داخلی نوشته شده به زبان C را حذف میکند و خصوصیتهای جادویی و بسیاری از متدهای غیر جادویی باقی میمانند. در اغلب موارد متدها و خصوصیتهای جادویی مواردی هستند که میخواهیم حذف کنیم و از این رو از روش فیلترینگ رشته استفاده خواهیم کرد.
جداسازی متدها از خصوصیتها
از میان آیتمهایی که پس از فیلترینگ به دست میآیند، ما همچنان نمیدانیم که کدام یک خصوصیت و کدام یک متد هستند. بدین منظور میتوان از تابع داخلی callable (https://docs.python.org/3/library/functions.html#callable) برای فیلتر کردن آنها استفاده کرد.
خصوصیتها
متدها
برای دیدن مقادیر خصوصیتها
فراخوانی متدها
در مورد متدها دیدن مقادیر خروجی چندان ساده نیست. یکی از ریسکهای فراخوانی گاهبهگاه متدهای تصادفی این است که ممکن است حالت شیء اصلی را تغییر دهند. برای نمونه Rectangle.bisect مقدار None بازگشت خواهد داد، اما اندازه مستطیل را با ضریبی از 2 کاهش میدهد.
میتوان با ایجاد یک copy.deepcopy پیش از هر فراخوانی متد، از بروز تغییراتی در شیء اصلی جلوگیری کرد، گرچه این وضعیت میتواند در مورد اشیای بزرگ از نظر محاسباتی سنگین باشد. توجه داشته باشید که متدهایی که متغیرهای کلاس یا متغیرهای سراسری را تغییر میدهند و یا با محیط بیرونیشان تعامل دارند ممکن است تأثیرات پایداری داشته باشند.
تابع get_callable که در ادامه تعریف شده است، شیء اصلی را کپی کرده و متد متصل به آن کپی را بازگشت میدهد که میتواند به صورت مستقل از شیء والدش فراخوانی شود.
متدهایی مانند Rectangle.scale که نیازمند پارامترهای موقعیتی (positional) باشند، چالشی مضاعف ایجاد میکنند.
ما میتوانیم خروجیهای متدهایی که نیازمند موقعیتهای مختلف هستند را با استفاده از «بررسی همهجانبه قبلی» یا با استفاده از gestfullargspec از ماژول داخلی insepct به دست آوریم تا مشخص شود که کدام اشیا نیازمند آرگومانهای موقعیتی نیستند و تنها آنها را ارزیابی کنیم.
تکنیک 1 فراخوانی متدها: بررسی همهجانبه
چنان که انتظار میرود، area و bisect با موفقیت اجرا شدهاند، در حالی که scale که نیازمند آرگومانهای موقعیتی دیگری است چنین نبوده است.
تکنیک 2 فراخوانی متدها: بررسی جایگاهها
ابتدا باید با getfullargspec آشنا شویم:
این متد یک شیء FullArgSpec بازگشت میدهد. args شامل نامهای آرگومانها است. vargs و varkw شامل نامهای آرگومانهای طول متغیر و آرگومانهای کلیدواژه است که به ترتیب با استفاده از عملگرهای * و ** مشخص میشوند. defaults شامل مقادیر پیشفرض برای آرگومانهای کلیدواژه است. kwonlyargs اسامی آرگومانهای صرفا-کلیدواژه را لیست میکند. kwonlydefaults یک دیکشنری است که دارای مقادیر پیشفرض آرگومان صرفاً-کلیدواژه است. annotations یک دیکشنری است که همه «حاشیهنویسیهای نوع» (type annotations) را تعیین میکند.
میتوان از این اطلاعات برای بررسی این نکته که آیا یک متد، آرگومانهای موقعیتی دارد یا نه، استفاده کرد و تنها در صورت عدم وجود چنین چیزی آن را ارزیابی کرد. در آغاز تلاش خواهیم کرد FullArgSpec متد را به دست آوریم گرچه همه انواع قابل فراخوانی پشتیبانی نمیشوند. سپس آرگومانها را استخراج میکنیم و یک تابع کاربردی به نام remove_self_ برای حذف آرگومان selft تعریف میکنیم که در متدهای استاندارد تصریح شده است. با این که این کار در اینجا انجام نیافته، اما میتوانیم با بررسی آرگومان cls از فراخوانی متدهای کلاس اجتناب کنیم. در نهایت اگر آرگومانها مقادیر پیشفرضی داشته باشند، در این صورت هیچ آرگومان موقعیتی وجود ندارد و لذا متد میتواند فراخوانی شود.
با استفاده از این روش همان نتایجی که در روش بررسی همه جانبه به دست امد را کسب میکنیم:
استنباط انواع آرگومان
در ادامه تلاش میکنیم نوع هر آرگومان را از روی نوع حاشیهنویسی مقادیر پیشفرض به دست آوریم. ما infer_arg_types را تعریف میکنیم که به صورتی مشابه call_if_no_positionals آغاز میشود، اما به جای فراخوانی متد، یک OrderedDict با انواع استنباط شده مقداردهی میکند.
این را روی وهلهای از Rectangle خود فرامیخوانیم تا انواع همه متدهایی که نیازمند آرگومان هستند به دست آید چون همه آنها دارای «سرنخ نوع» (type hint) هستند. توجه کنید که اگر دارای سرنخ نوع نبودند، این ترفند تنها برای آرگومانهای کلیدواژه کار میکرد.
ساخت آرگومانها
اگر بخواهیم خروجیهای نمونه متدهایی که نیازمند آرگومانها موقعیتی هستند را ببینیم، میتوانیم از انواع آرگومان که در بخش قبل استنباط شده است استفاده کنیم تا آنها را با بررسی مقادیر نمونه برای هر نوع بسازیم. حتی میتوان در صورت وجود نوع محتوا در حاشیهنویسی حتی تلاش کنیم collection نیز بسازیم.
ما یک ForgeError را طوری تعریف میکنیم که هر خطای ناشی از ساخت آرگومان را بتوان به صورت دقیق مدیریت کرد. بدین ترتیب میتوانیم آرگومانها را برای یک کلکسیون از متدها، حتی اگر برخی از آنها کار نکنند بسازیم.
برای ساخت تابع، یک متد برمیداریم و آرگومانهای نمونه را از sample_args_ برحسب نوع خروجی infer_arg_types بررسی میکنیم. در صورتی که هر نوع آرگومانی فاقد نوع پیشفرض باشند و نوعها را نتوان استنباط کرد، خطاهایی رخ میدهند یا اگر هر نوعی وجود داشته باشند، در sample_args_ وجود نخواهند داشت.
از آنجا که این تابع نسبتاً پیچیدهای است، این وضعیت میتواند جای خوبی برای برخی تستهای unit باشد.
سپس میتوانیم تابعی تعریف کنیم که یک شیء بگیرد و روی همه متدهای آن بچرخد. این تابع از تابع forge_args ما برای ساخت آرگومانها در مواردی که از رویکرد «بررسی همهجانبه» استفاده میکنند بهره میگیرد و به بررسی دلایل هر گونه شکست میپردازد.
در ادامه آن را روی وهله Rectangle امتحان میکنیم:
تفاوت بین این نتیجه و نتیجه قبلی ما چندان بزرگ نیست، اما توجه کنید که scale هم اینک به جای ‘requires positional args’ مقدار ‘None’ را در خروجی ارائه میدهد. دلیل این امر آن است که این متد با موفقیت با آرگومانهای ساخته شده فراخوانی شده است، اما به جای بازگشت دادن هیچ چیز، حالت rect را با تغییر دادن خصوصیت a و b عوض میکند. ردگیری این تغییرات به طوری که بتوان کاری که متدها انجام میدهند را حتی در زمانی که هیچ چیزی بازگشت نمیدهند تشخیص داد بسیار مناسب خواهد بود.
ردگیری تغییرات حالت: تکنیک مقایسه
در این مثال نمونه، بُعد یعنی a و b را از Rectangle تغییر میدهیم، اما از آنجا که متد هیچ بازگشتی ندارد تعیین این که چه اتفاقی افتاده است کار دشواری است. ما میتوانیم این تغییرات را با ذخیره کردن یک کپی از همه خصوصیات شیء پیش از فراخوانی متد و پس از آن مقایسه کردن دریابیم. بدین ترتیب شیء StateComparator را طوری تعریف میکنیم که امکان ذخیره خصوصیتهای جاری را با استفاده از خصوصیت __dict__ به دست دهد، سپس موارد خصوصیتهای حذف، اضافه و تغییر یافته را پس از فراخوانی متد بررسی میکنیم.
پیادهسازی واقعی کمی پیچیدهتر است، زیرا اغلب موارد داخلی دارای خصوصیت __dict__ نیستند.
ما با استفاده از تابع forge_and_eval_methods به عنوان یک قالب میتوانیم یک تابع جدید تعریف کنیم که شامل ردگیری حالت و گزینهای برای فعال یا غیرفعال کردن ساخت آرگومان باشد.
در ادامه این مورد را روی rect تست میکنیم:
اکنون هر تغییر حالت در دیکشنری تعیین شده است و تغییرات به وسیله یک چندتایی که عدد اول نشاندهنده مقدار اولیه و عدد دوم نماینده مقدار نهایی است نمایش مییابند.
متأسفانه ساختن آرگومان از آرگومانهای کلیدواژه و حاشیهنویسی کار دشواری است، زیرا اغلب کدهای پایتون دارای سرنخ نوع نیستند و بخش بزرگ آن از سوی getargspec پشتیبانی نمیشوند. در این موارد، ساخت آرگومان از طریق روش brute force یا استخراج از docstring-ها نیز ممکن است برای قابلیتهای آتی peep dis برنامهریزیشده است.
پرینت کردن صرف docstring-ها راه آسانتری برای درک متدهایی است که در اغلب موارد نیازمند آرگومان هستند. آنها را میتوان به طور سیستماتیک از خصوصیت __doc__ پرینت کرد.
گنجاندن خروجی بسیار طولانی است و رمزگشایی از آن دشوار است، زیرا دارای کدهای رنگی نیست. خروجی میتواند با termcolor رنگآمیزی شود که برای peep dis نیز استفاده شود.
بازبینی شیء CLI: Peep Dis
در این بخش به بررسی peep dis میپردازیم که در دو حالت مفید واقع میشوند.
شیء اسرارآمیز
ما یک شیء ساده به نام mystery_obj داریم که شامل آرایهای از دماهای مکانی در سانفرانسیسکو است، اما نمیدانیم آنجا کجاست. میتوانیم dir را فراخوانی کنیم و سپس به صورت تکراری هر متد یا خصوصیت را بررسی کنیم. همچنین میتوانیم شیء را peep نیز بکنیم. ما میتوانیم stdtemp را به عنوان خصوصیتی که نیاز داریم به سرعت شناسایی کنیم.
موارد داخلی فیلتر میشوند و خروجیها برای بقیه خصوصیتها و متدها بدون آرگومان موقعیتی پرینت میشوند. متدها به رنگ بنفش و خصوصیتها فیروزهای رنگآمیزی شدهاند. خروجی متدهای نیازمند آرگومان موقعیتی خاکستری میشوند تا امکان شناسایی راحتتر بقیه موارد را پیدا کنیم.
نام متد چیست؟
ما یک DataFrame با ستونهای temp و humidity برای سانفرانسیسکو داریم که میخواهیم به یک مدل داده ظریف برای یک API که میسازیم تبدیل کنیم. یک روش خلاصه برای این کار وجود دارد، اما چیزی از dir مشخص نمیشود و از Stack Overflow نیز نتیجهای عاید نمیشود. اگر DataFrame را peep کنیم، به سرعت مشخص میشود که melt آن متدی است که نیاز داریم.
در ادامه روشهای قدیمی انجام این کارها را بررسی میکنیم:
شیء mystery
نام متد
ممکن است ده دقیقه طول بکشد تا متوجه شویم که آن چه نیاز داریم df.melt است.
بدین ترتیب به پایان این مقاله میرسیم. امیدواریم از مطالعه این راهنما بهره کافی را برده باشید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی پایتون Python
- گنجینه آموزشهای برنامهنویسی پایتون (Python)
- مجموعه آموزشهای برنامهنویسی
- ۱۰ گام تکمیلی ایجاد پروژه متن باز پایتون — راهنمای کاربردی
- زبان برنامه نویسی پایتون (Python) — از صفر تا صد
==