از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
کتاب دکوراتور پایتون
سرفصلهای مطلب
دکوراتورهای پایتون یک نحو ساده و در عین حال قدرتمند برای اصلاح و گسترش رفتار توابع در کد شما ارائه میکنند.
دکوراتور اساساً عملکردی است که عملکرد دیگری را می گیرد، عملکرد آن را افزایش می دهد و عملکرد جدیدی را برمی گرداند – بدون اینکه خود عملکرد اصلی را به طور دائم تغییر دهد.
این آموزش شما را با 11 دکوراتور مفید آشنا میکند تا به افزودن قابلیتهایی مانند اجرای زمانبندی، حافظه پنهان، محدود کردن نرخ، اشکالزدایی و موارد دیگر کمک کند. چه بخواهید مشخصات عملکرد، بهبود کارایی، اعتبارسنجی داده ها یا مدیریت خطاها را داشته باشید، این دکوراتورها شما را تحت پوشش قرار داده اند!
مثالهای اینجا تمرکز دارند روی الگوهای استفاده متداول و کاربردهای دکوراتورها که می تواند در برنامه ریزی روزانه شما مفید واقع شود و در تلاش شما صرفه جویی کند. درک انعطاف پذیری دکوراتورها به شما کمک می کند تا کد برنامه تمیز، انعطاف پذیر و بهینه را بنویسید.
فهرست مطالب
دکوراتورهایی که در این آموزش به آنها پرداخته شده است:
- گزارش آرگومان ها و مقدار بازگشتی یک تابع
- زمان اجرای یک تابع را دریافت کنید
- مقدار بازگشتی تابع را به یک نوع داده مشخص تبدیل کنید
- نتایج عملکرد حافظه پنهان
- اعتبار سنجی تابع آرگومان های مبتنی بر روی وضعیت
- یک تابع را چندین بار دوباره امتحان کنید روی شکست
- اعمال محدودیت های نرخ روی یک تابع
- موارد استثنا را مدیریت کنید و پاسخ پیش فرض را ارائه دهید
- بررسی نوع را اجرا کنید روی آرگومان های تابع
- اندازه گیری میزان استفاده از حافظه از یک تابع
- نتایج عملکرد حافظه پنهان با زمان انقضا
- نتیجه
اما ابتدا یک مقدمه کوچک.
روش کار دکوراتورهای پایتون
قبل از غواصی، بیایید برخی از مزایای کلیدی دکوراتورها در پایتون را درک کنیم:
- بهبود عملکردها بدون تغییرات تهاجمی: دکوراتورها عملکردهای شفاف را بدون تغییر کد اصلی افزایش می دهند و منطق اصلی را تمیز و قابل نگهداری نگه می دارند.
- استفاده مجدد از عملکرد در مکان ها: قابلیتهای رایج مانند ورود به سیستم، ذخیرهسازی ذخیرهسازی و محدود کردن نرخ را میتوان یک بار در دکوراتورها ایجاد کرد و در هر کجا که لازم بود اعمال کرد.
- نحو خواندنی و اعلانی: این
@decorator
نحو به سادگی بهبود عملکرد را در سایت تعریف منتقل می کند. - مدولار بودن و تفکیک نگرانی ها: دکوراتورها اتصال آزاد بین منطق عملکردی و قابلیت های ثانویه مانند عملکرد، امنیت، ورود به سیستم و غیره را ترویج می کنند.
نکته مهم این است که دکوراتورها راههای ساده و در عین حال انعطافپذیر را برای افزایش شفاف توابع پایتون برای بهبود سازماندهی کد، کارایی و استفاده مجدد بدون ایجاد پیچیدگی یا افزونگی باز میکنند.
در اینجا یک مثال اساسی از نحو دکوراتور در پایتون با حاشیه نویسی آمده است:
دکوراتور در پایتون تابعی است که تابع دیگری را به عنوان آرگومان می گیرد و رفتار خود را بدون تغییر آن گسترش می دهد. تابع دکوراتور عملکرد اصلی را با تعریف یک تابع پوشش در داخل آن میپیچد. این تابع wrapper کد را قبل و بعد از فراخوانی تابع اصلی اجرا می کند.
به طور خاص، هنگام تعریف یک عملکرد تزئینی مانند my_decorator
در مثال، تابعی را به عنوان آرگومان می گیرد که معمولاً آن را فراخوانی می کنیم func
. این func
عملکرد واقعی خواهد بود که زیر کاپوت تزئین شده است.
عملکرد لفاف در داخل my_decorator
می تواند کد دلخواه را قبل و بعد از فراخوانی اجرا کند func()
، که تابع اصلی را فراخوانی می کند. هنگام درخواست @my_decorator
قبل از تعریف my_function
، می گذرد my_function
به عنوان یک استدلال برای my_decorator
، بنابراین func به my_function
در آن زمینه
سپس تابع wrapper تابع wrapped پیشرفته را برمی گرداند. بنابراین در حال حاضر my_function
توسط تزئین شده است my_decorator
. وقتی بعداً نامیده می شود، کد wrapper در داخل است my_decorator
قبل و بعد اجرا می کند my_function
دویدن. این به دکوراتورها اجازه می دهد تا رفتار یک عملکرد را بدون نیاز به تغییر خود عملکرد، به طور شفاف گسترش دهند.
و همانطور که به یاد دارید، اصلی است my_function
بدون تغییر باقی می ماند و دکوراتورها را غیر تهاجمی و انعطاف پذیر نگه می دارد.
چه زمانی my_function()
تزئین شده است @my_decorator
، به طور خودکار افزایش می یابد. این my_decorator
تابع در اینجا یک تابع wrapper برمی گرداند. این تابع wrapper زمانی اجرا می شود که my_function()
اکنون نامیده می شود.
ابتدا لفاف چاپ می شود "Before the function call"
قبل از تماس واقعی با نسخه اصلی my_function()
عملکرد در حال تزئین پس از my_function()
اجرا می کند، چاپ می کند "After function call"
.
بنابراین، رفتار اضافی و پیام های چاپی قبل و بعد از آن اضافه می شوند my_function()
اجرا در wrapper، بدون تغییر مستقیم my_function()
خود دکوراتور به شما اجازه می دهد تا گسترش دهید my_function()
به روشی شفاف بدون اینکه بر منطق اصلی آن تأثیر بگذارد، زیرا لفاف دار رفتار بهبود یافته را کنترل می کند.
پس بیایید شروع به کاوش در مورد 11 دکوراتور کاربردی کنیم که هر توسعه دهنده پایتون باید آنها را بشناسد.
گزارش آرگومان ها و مقدار بازگشتی یک تابع
دکوراتور Log Arguments and Return Value پارامترهای ورودی و خروجی توابع را ردیابی می کند. این از اشکال زدایی با ثبت یک رکورد واضح از جریان داده از طریق عملیات پیچیده پشتیبانی می کند.
خروجی:
Calling calculate_product with args: (10, 20), kwargs: {}
calculate_product returned: 200
Result: 200
در این مثال تابع دکوراتور نامگذاری شده است log_decorator()
و یک تابع را می پذیرد، original_function
، به عنوان استدلال آن. در داخل log_decorator()
، یک تابع تودرتو به نام wrapper()
تعریف شده است. این wrapper()
عملکرد همان چیزی است که دکوراتور باز می گرداند و به طور موثر جایگزین عملکرد اصلی می شود.
وقتی که wrapper()
تابع فراخوانی می شود، بیانیه های گزارش مربوط به فراخوانی تابع را چاپ می کند. سپس تابع اصلی را فراخوانی می کند، original_function
، نتیجه خود را می گیرد، نتیجه را چاپ می کند و نتیجه را برمی گرداند.
این @log_decorator
نحو بالای calculate_product()
تابع یک قرارداد پایتون برای اعمال است log_decorator
به عنوان یک دکوراتور به calculate_product
تابع. بنابراین، هنگامی که calculate_product()
فراخوانی شده است، در واقع در حال فراخوانی است wrapper()
تابع برگردانده شده توسط log_decorator()
. از این رو، log_decorator()
به عنوان یک wrapper عمل می کند و دستورات ورود به سیستم را قبل و بعد از اجرای نسخه اصلی معرفی می کند. calculate_product()
تابع.
استفاده و کاربردها
این دکوراتور به طور گسترده در توسعه برنامه برای اضافه کردن گزارش زمان اجرا بدون تداخل با اجرای منطق تجاری استفاده می شود.
به عنوان مثال، یک برنامه بانکی را در نظر بگیرید که تراکنش های مالی را پردازش می کند. منطق پردازش تراکنش اصلی در توابعی مانند قرار دارد transfer_funds()
و accept_payment()
. برای نظارت بر این تراکنش ها، ورود به سیستم را می توان با اضافه کردن اضافه کرد @log_decorator
بالای هر تابع
سپس هنگامی که تراکنش ها با تماس آغاز می شوند transfer_funds()
، تو می توانی print نام تابع، آرگومان هایی مانند فرستنده، گیرنده و مقدار قبل از انتقال واقعی. سپس پس از بازگشت تابع، می توانید print اینکه آیا این انتقال موفقیت آمیز بوده یا شکست خورده است.
این نوع ورود به سیستم با دکوراتورها به شما امکان می دهد تا تراکنش ها را بدون افزودن کد به عملکردهای اصلی مانند ردیابی کنید transfer_funds()
. منطق پاک می ماند در حالی که اشکال زدایی و مشاهده پذیری بهبود می یابد. پیامهای ثبتنام را میتوان به داشبورد نظارت یا سیستم تجزیه و تحلیل گزارش نیز هدایت کرد.
زمان اجرای یک تابع را دریافت کنید
این دکوراتور متحد شما در تلاش برای بهینه سازی عملکرد است. با اندازهگیری و ثبت زمان اجرای یک تابع، این دکوراتور بررسی عمیق کارایی کد شما را تسهیل میکند و به شما کمک میکند تنگناها را مشخص کنید و عملکرد برنامه خود را ساده کنید.
برای سناریوهایی که سرعت بسیار مهم است، مانند برنامه های کاربردی بلادرنگ یا پردازش داده در مقیاس بزرگ، ایده آل است. و به شما این امکان را می دهد که گلوگاه های عملکرد را به طور سیستماتیک شناسایی و برطرف کنید.
خروجی:
Function multiply_numbers took 0.00 seconds to execute
Result: 362880
این کد یک دکوراتور را نشان می دهد که برای اندازه گیری مدت زمان اجرای عملکردها طراحی شده است.
این measure_execution_time()
دکوراتور عملکردی را بر عهده می گیرد، func
و یک تابع درونی را تعریف می کند، timed_execution()
، برای بسته بندی تابع اصلی. پس از فراخوان، timed_execution()
زمان شروع را ضبط می کند، تابع اصلی را فراخوانی می کند، زمان پایان را ثبت می کند، مدت زمان را محاسبه می کند و آن را چاپ می کند.
این @measure_execution_time
نحو این دکوراتور را برای توابع زیر آن اعمال می کند، مانند multiply_numbers()
. در نتیجه، زمانی که multiply_numbers()
نامیده می شود، آن را فرا می خواند timed_execution()
wrapper، که مدت زمان را در کنار نتیجه عملکرد ثبت می کند.
این مثال نشان میدهد که چگونه دکوراتورها به طور یکپارچه عملکردهای موجود را با عملکردهای اضافی، مانند زمانبندی، بدون تغییر مستقیم افزایش میدهند.
استفاده و کاربردها
این دکوراتور در عملکردهای پروفایل برای شناسایی گلوگاه های عملکرد در برنامه ها مفید است. به عنوان مثال، یک سایت تجارت الکترونیکی با چندین عملکرد پشتیبان مانند در نظر بگیرید get_recommendations()
، calculate_shipping()
، و غیره روی. با تزئین آنها با @measure_execution_time
، می توانید زمان اجرای آنها را نظارت کنید.
چه زمانی get_recommendations()
در یک جلسه کاربر فراخوانی می شود، دکوراتور مدت زمان اجرای آن را با ثبت مهر زمانی شروع و پایان زمان بندی می کند. پس از اجرا خواهد شد print زمان صرف شده قبل از بازگشت توصیه ها
انجام این کار به طور سیستماتیک در بین برنامهها و تجزیه و تحلیل خروجیها، عملکردهایی را به شما نشان میدهد که زمان زیادی طول میکشد. سپس تیم توسعه می تواند چنین توابعی را از طریق کش کردن، پردازش موازی و سایر تکنیک ها برای بهبود عملکرد کلی برنامه بهینه کند.
بدون چنین دکوراتورهای زمانبندی، یافتن نامزدهای بهینهسازی نیازمند افزودن کدهای لاگ خستهکننده است. دکوراتورها بدون ایجاد آلودگی منطق تجاری، دید را به راحتی فراهم می کنند.
مقدار بازگشتی تابع را به یک نوع داده مشخص تبدیل کنید
دکوراتور Convert Return Value Type با تبدیل خودکار مقدار بازگشتی به یک نوع داده مشخص، سازگاری داده ها را در عملکردها افزایش می دهد، پیش بینی پذیری را ارتقا می دهد و از خطاهای غیرمنتظره جلوگیری می کند. این به ویژه برای فرآیندهای پایین دستی که به انواع داده های ثابت نیاز دارند مفید است و خطاهای زمان اجرا را کاهش می دهد.
خروجی:
Result: 30 <class 'int'>
Result: Python Decorator <class 'str'>
مثال کد بالا یک دکوراتور را نشان می دهد که برای تبدیل مقدار بازگشتی یک تابع به یک نوع داده مشخص طراحی شده است.
دکوراتور، به نام convert_to_data_type()
، نوع داده مورد نظر را به عنوان پارامتر می گیرد و یک دکوراتور با نام را برمی گرداند type_converter_decorator()
. در این دکوراتور، یک wrapper()
تابع برای فراخوانی تابع اصلی، تبدیل مقدار بازگشتی آن به نوع هدف با استفاده از آن تعریف شده است target_type()
، و متعاقباً نتیجه تبدیل شده را برمی گرداند.
نحو @convert_to_data_type(int)
که در بالای یک تابع اعمال می شود (مانند add_values()
) از این تزئین کننده برای تبدیل مقدار برگشتی به یک عدد صحیح استفاده می کند. به طور مشابه، برای concatenate_strings()
، گذراندن str
مقدار برگشتی را به صورت رشته فرمت می کند.
این مثال همچنین نشان می دهد که چگونه دکوراتورها به طور یکپارچه خروجی عملکرد را به فرمت های دلخواه تغییر می دهند بدون اینکه منطق اصلی توابع را تغییر دهند.
استفاده و کاربرد
این دکوراتور تبدیل ارزش بازگشتی در برنامههایی که باید به طور خودکار عملکردها را با قالبهای داده مورد انتظار تطبیق دهید مفید است.
به عنوان مثال، می توانید از آن در یک API آب و هوا استفاده کنید که دما را به طور پیش فرض در قالب اعشاری مانند 23.456 درجه برمی گرداند. اما برنامه کاربردی مصرف کننده انتظار دارد یک مقدار صحیح نمایش داده شود.
به جای تغییر تابع API برای برگرداندن یک عدد صحیح، فقط آن را با آن تزئین کنید @convert_to_data_type(int)
. این به طور یکپارچه دمای اعشاری را به عدد صحیح تبدیل می کند 23
، در این مثال، قبل از بازگشت به برنامه مشتری. بدون هیچ تغییری در عملکرد API، مقدار بازگشتی را دوباره قالببندی کردهاید.
به طور مشابه برای پردازش باطن مورد انتظار JSON، مقادیر بازگشتی را می توان با استفاده از آن تبدیل کرد @convert_to_data_type(json)
دکوراتور منطق اصلی بدون تغییر باقی می ماند در حالی که قالب ارائه بر اساس تطبیق می یابد روی نیازهای مورد استفاده شما این از تکرار کدهای مدیریت فرمت در توابع جلوگیری می کند.
دکوراتورها از بیرون نمایش داده های مورد نیاز را برای یکپارچه سازی یکپارچه و قابلیت استفاده مجدد در لایه های برنامه با فرمت های ناهماهنگ تحمیل می کنند.
نتایج عملکرد حافظه پنهان
این دکوراتور با ذخیره و بازیابی نتایج عملکرد، حذف محاسبات اضافی برای ورودی های مکرر، و بهبود پاسخگویی برنامه، به ویژه برای محاسبات وقت گیر، عملکرد را بهینه می کند.
خروجی:
Product = 20
[FROM CACHE] Product = 20
Product = 35
[FROM CACHE] Product = 35
Product = -21
[FROM CACHE] Product = -21
این نمونه کد یک دکوراتور را نشان می دهد که برای ذخیره و استفاده مجدد از نتایج فراخوانی عملکرد به طور موثر طراحی شده است.
این cached_result_decorator()
تابع تابع دیگری را می گیرد و یک wrapper برمی گرداند. در این بسته بندی، یک فرهنگ لغت حافظه پنهان (result_cache
) پارامترهای تماس منحصر به فرد و نتایج مربوط به آنها را ذخیره می کند.
قبل از اجرای تابع واقعی، wrapper()
بررسی می کند که آیا نتیجه پارامترهای فعلی از قبل در حافظه پنهان است. اگر چنین است، نتیجه ذخیره شده را بازیابی و برمی گرداند – در غیر این صورت، تابع را فراخوانی می کند، نتیجه را در حافظه پنهان ذخیره می کند و آن را برمی گرداند.
این @cached_result_decorator
سینتکس این منطق کش را برای هر تابعی مانند multiply_numbers()
. این تضمین می کند که پس از فراخوانی های بعدی با همان آرگومان ها، نتیجه ذخیره شده در حافظه پنهان مجددا استفاده می شود و از محاسبات اضافی جلوگیری می کند.
در اصل، دکوراتور با بهینهسازی عملکرد از طریق ذخیرهسازی نتایج، عملکرد را افزایش میدهد.
استفاده و کاربردها
دکوراتورهای ذخیره سازی مانند این در توسعه برنامه برای بهینه سازی عملکرد فراخوانی عملکردهای تکراری بسیار مفید هستند.
به عنوان مثال، یک موتور توصیه را در نظر بگیرید که توابع مدل پیش بینی را برای ایجاد پیشنهادات کاربر فراخوانی می کند. get_user_recommendations()
دادههای ورودی را آماده میکند و برای هر درخواست کاربر به مدل وارد میکند. بهجای اجرای مجدد محاسبات، میتوان آن را با @cached_result_decorator
برای معرفی لایه کش
اکنون اولین باری که پارامترهای کاربر منحصر به فرد ارسال می شود، مدل اجرا می شود و نتایج ذخیره می شود. تماسهای بعدی با ورودیهای یکسان مستقیماً خروجیهای مدل حافظه پنهان را برمیگردانند و از محاسبه مجدد مدل صرفنظر میکنند.
این امر تأخیر پاسخ به درخواستهای کاربر را با اجتناب از استنباطهای مدل تکراری به شدت بهبود میبخشد. برای توجیه کاهش هزینههای زیرساخت سرور مدل، میتوانید نرخ ضربه حافظه پنهان را نظارت کنید.
جدا کردن چنین نگرانیهای بهینهسازی از طریق دکوراتورهای کش به جای اختلاط آنها در منطق عملکرد، ماژولار بودن، خوانایی را بهبود میبخشد و به افزایش عملکرد سریع اجازه میدهد. حافظه پنهان پیکربندی میشود و بهطور جداگانه بدون ایجاد توابع مزاحم تجاری باطل میشود.
اعتبار سنجی تابع آرگومان های مبتنی بر روی وضعیت
این یکی بررسی می کند که آیا آرگومان های ورودی قبل از اجرا با معیارهای از پیش تعریف شده مطابقت دارند یا خیر، قابلیت اطمینان عملکرد را افزایش می دهد و از رفتار غیرمنتظره جلوگیری می کند. برای پارامترهایی که به اعداد صحیح مثبت یا رشته های غیر خالی نیاز دارند مفید است.
خروجی:
125Traceback (most recent call last):
File "C:\\\\Program Files\\\\Sublime Text 3\\\\test.py", line 16, in <module>
print(compute_cubed_result(-2)) # Raises ValueError: Invalid arguments passed to the function
File "C:\\\\Program Files\\\\Sublime Text 3\\\\test.py", line 7, in validate_and_calculate
raise ValueError("Invalid arguments passed to the function")
ValueError: Invalid arguments passed to the function
این کد نشان می دهد که چگونه می توانید یک دکوراتور را برای اعتبار آرگومان های تابع پیاده سازی کنید.
این check_condition_positive()
یک کارخانه دکوراتور است که یک argument_validator()
دکوراتور این اعتبار سنج، زمانی که با @check_condition_positive()
بالای compute_cubed_result()
تابع، بررسی می کند که آیا شرط (در این مورد، که آرگومان باید بزرگتر از 0 باشد) برای آرگومان های ارسال شده صادق است یا خیر.
اگر شرط برآورده شود، تابع تزئین شده اجرا می شود – در غیر این صورت، a ValueError
استثنا مطرح می شود.
این مثال مختصر نشان میدهد که چگونه دکوراتورها بهعنوان مکانیزمی برای اعتبار آرگومانهای تابع قبل از اجرایشان عمل میکنند و از پایبندی به شرایط مشخص اطمینان میدهند.
استفاده و کاربردها
چنین تزیین کننده های اعتبارسنجی پارامتر در برنامه های کاربردی برای کمک به اجرای قوانین تجاری، محدودیت های امنیتی و غیره بسیار مفید هستند. روی.
به عنوان مثال، یک سیستم پردازش خسارت بیمه عملکردی دارد process_claim()
که جزئیاتی مانند شناسه ادعا، نام تأیید کننده و غیره را می گیرد روی. برخی از قوانین تجاری تعیین می کنند که چه کسی می تواند ادعاها را تأیید کند.
به جای درهم ریختن منطق تابع، می توانید آن را با آن تزئین کنید @check_condition_positive()
در صورتی که نقش تأیید کننده با مبلغ ادعا مطابقت داشته باشد اعتبار می دهد. اگر یک نماینده جوان سعی کند ادعای بزرگی را تأیید کند (در نتیجه قوانین را نقض می کند)، این دکوراتور آن را با مطرح کردن استثنا حتی قبل از آن می گیرد. process_claim()
اجرا می کند.
به طور مشابه، محدودیتهای اعتبارسنجی داده ورودی برای امنیت و انطباق را میتوان بدون دست زدن به عملکردهای فردی اعمال کرد. دکوراتورها از بیرون اطمینان می دهند که استدلال های نقض شده هرگز به خطرات کاربردی نمی رسند.
الگوهای اعتبارسنجی متداول باید در چندین تابع مورد استفاده مجدد قرار گیرند. این امر امنیت را بهبود می بخشد و با جداسازی محدودیت ها از جریان منطق اصلی به روش مدولار، جداسازی نگرانی ها را ترویج می کند.
یک تابع را چندین بار دوباره امتحان کنید روی شکست
این دکوراتور زمانی مفید است که بخواهید به طور خودکار عملکردی را پس از شکست دوباره امتحان کنید و انعطاف پذیری آن را در شرایطی که شامل خرابی های گذرا است افزایش دهید. برای سرویس های خارجی یا درخواست های شبکه مستعد خرابی های متناوب استفاده می شود.
خروجی:
Error occurred: no such table: users. Retrying...
Error occurred: no such table: users. Retrying...
Error occurred: no such table: users. Retrying...
Failed to establish database connection: Maximum attempts exceeded. Function failed.
این مثال یک دکوراتور را معرفی می کند که برای اجرای مجدد عملکرد در صورت خرابی طراحی شده است. حداکثر تعداد تلاش و تأخیر بین تلاشهای مجدد مشخص شده است.
این retry_on_failure()
یک کارخانه دکوراتور است که پارامترهایی را برای حداکثر تعداد و تاخیر تلاش مجدد می گیرد و a را برمی گرداند decorator()
که منطق تلاش مجدد را مدیریت می کند.
در داخل wrapper()
تابع، تابع تزئین شده در یک حلقه اجرا می شود و حداکثر تعداد مشخصی بارها انجام می شود.
در صورت استثنا، پیغام خطا را چاپ می کند، تاخیر مشخص شده توسط را معرفی می کند retry_delay
، و دوباره تلاش می کند. اگر همه تلاشها با شکست مواجه شوند، استثنایی ایجاد میکند که نشان میدهد از حداکثر تلاشها فراتر رفته است.
این @retry_on_failure()
در بالا اعمال شد establish_database_connection()
این منطق تلاش مجدد را ادغام میکند، تا در صورت مواجهه با شکست اتصال پایگاه داده، حداکثر 3 بار با تاخیر 2 ثانیهای بین هر تلاش امکانپذیر است.
این نشاندهنده کاربرد دکوراتورها در ترکیب یکپارچه قابلیتهای امتحان مجدد بدون تغییر کد عملکرد اصلی است.
استفاده و کاربرد
این دکوراتور میتواند در توسعه برنامهها برای افزایش انعطافپذیری در برابر خطاهای موقت یا متناوب بسیار مفید باشد.
به عنوان مثال، یک برنامه رزرو پرواز را در نظر بگیرید که API درگاه پرداخت را صدا می کند process_payment()
برای رسیدگی به معاملات مشتری گاهی اوقات قطع شدن شبکه یا بارهای زیاد در انتهای ارائه دهنده پرداخت می تواند باعث خطاهای گذرا در پاسخ API شود.
به جای نشان دادن مستقیم شکست به مشتریان، process_payment()
عملکرد را می توان با تزئین کرد @retry_on_failure
برای رسیدگی به چنین سناریوهایی به طور ضمنی. اکنون هنگامی که پرداخت یک بار با شکست مواجه می شود، به طور یکپارچه مجدداً درخواست را تا 3 بار ارسال می کند تا در صورت تداوم خطا، در نهایت آن را گزارش کند.
این امر بدون اینکه کاربران را مستقیماً در معرض رفتارهای زیرساختی نامطمئن قرار دهد، از سکسکههای موقت محافظت میکند. برنامه همچنین بهطور قابل اعتمادی در دسترس باقی میماند حتی اگر سرویسهای وابسته گهگاهی از کار بیفتند.
دکوراتور کمک می کند تا منطق امتحان مجدد را به طور منظم و بدون پخش آن در سراسر کد API محدود کند. خرابیهای خارج از کنترل برنامه به جای اینکه مستقیماً توسط خطاهای برنامه تأثیر بگذارند، به خوبی مدیریت میشوند. این نشان می دهد که چگونه دکوراتورها انعطاف پذیری بهتری را بدون ایجاد پیچیدگی منطق کسب و کار ارائه می دهند.
اعمال محدودیت های نرخ روی یک تابع
دکوراتور Enforce Rate Limits با کنترل فرکانس عملکردهای فراخوانی شده، مدیریت مؤثر منابع را تضمین می کند و از سوء استفاده محافظت می کند. این به ویژه در سناریوهایی مانند سوء استفاده از API یا حفظ منابع که محدود کردن فراخوانی عملکرد ضروری است مفید است.
خروجی:
API call executed successfully...
API call executed successfully...
API call executed successfully...
API call executed successfully...
API call executed successfully...
API call executed successfully...
Error occurred: Rate limit exceeded. Please try again later.
Error occurred: Rate limit exceeded. Please try again later.
API call executed successfully...
این کد اجرای یک مکانیسم محدود کننده نرخ را برای فراخوانی تابع با استفاده از دکوراتور نشان می دهد.
این rate_limiter()
تابع، مشخص شده با حداکثر تماس و یک دوره در ثانیه برای تنظیم مجدد تعداد، به عنوان هسته منطق محدود کننده نرخ عمل می کند. دکوراتور، decorate_rate_limited_function()
، از یک لفاف برای مدیریت محدودیت های نرخ با تنظیم مجدد شمارش در صورت سپری شدن دوره استفاده می کند. بررسی میکند که آیا تعداد به حداکثر مجاز رسیده است یا خیر، و سپس یک استثنا ایجاد میکند یا تعداد را افزایش میدهد و تابع را مطابق با آن اجرا میکند.
اعمال شده به make_api_call()
استفاده کردن @rate_limiter()
، این تابع را به شش تماس در هر دوره 10 ثانیه ای محدود می کند. این محدودیت نرخ را بدون تغییر منطق تابع معرفی میکند، اطمینان میدهد که تماسها به محدودیتها پایبند هستند و از استفاده بیش از حد در بازههای زمانی تعیینشده جلوگیری میکند.
استفاده و کاربرد
دکوراتورهای محدود کننده نرخ مانند این در توسعه برنامه برای کنترل استفاده از API و جلوگیری از سوء استفاده بسیار مفید هستند.
به عنوان مثال، یک برنامه رزرو سفر ممکن است متکی باشد روی API جستجوی پرواز شخص ثالث برای بررسی در دسترس بودن صندلی زنده در خطوط هوایی. در حالی که بیشتر استفادهها مشروع است، برخی از کاربران به طور بالقوه میتوانند این API را بیش از حد فراخوانی کنند و عملکرد کلی سرویس را کاهش دهند.
با تزئین ماژول ادغام API مانند @rate_limiter(100, 60)
، برنامه می تواند تماس های بیش از حد داخلی را نیز محدود کند. این باعث میشود که ماژول رزرو فقط 100 تماس API پرواز در دقیقه برقرار کند. تماسهای اضافی مستقیماً از طریق دکوراتور بدون رسیدن به API واقعی رد میشوند.
این امر سرویس پایین دستی را از استفاده بیش از حد نجات میدهد و توزیع عادلانهتر ظرفیت را برای عملکرد کلی برنامه امکانپذیر میسازد.
دکوراتورها کنترل نرخ آسان را برای APIهای داخلی و خارجی بدون تغییر کد عملکردی فراهم می کنند. این بدان معناست که لازم نیست در حین محافظت از خدمات، زیرساختها و ریسک پذیرش محدود، سهمیههای استفاده را در نظر بگیرید. و همه اینها به لطف کنترل های سمت برنامه با استفاده از بسته بندی است.
موارد استثنا را مدیریت کنید و پاسخ پیش فرض را ارائه دهید
دکوراتور Handle Exceptions یک شبکه ایمنی برای عملکردها است که استثنائات را با ظرافت مدیریت می کند و در صورت وقوع پاسخ های پیش فرض را ارائه می دهد. این برنامه از خراب شدن برنامه به دلیل شرایط پیش بینی نشده محافظت می کند و عملکرد روان را تضمین می کند.
خروجی:
Exception occurred: division by zero
Result: An error occurred!
این کد مدیریت استثنا در توابع با استفاده از دکوراتورها را نشان می دهد.
این handle_exceptions()
کارخانه دکوراتور با پذیرش یک پاسخ پیش فرض، تولید می کند exception_handler_decorator()
. این دکوراتور وقتی روی توابع اعمال می شود، سعی می کند عملکرد اصلی را اجرا کند. اگر یک استثنا ایجاد شود، جزئیات خطا را چاپ می کند و پاسخ پیش فرض مشخص شده را برمی گرداند.
این @handle_exceptions()
نحو بالای یک تابع، این منطق رسیدگی به استثنا را در بر می گیرد. به عنوان مثال، در divide_numbers_safely()
، تقسیم بر صفر یک استثنا را ایجاد می کند که تزئین کننده آن را می گیرد و از خرابی جلوگیری می کند و پیش فرض “خطایی رخ داده است!” واکنش.
اساساً، این دکوراتورها به طرز ماهرانهای استثناها را در عملکردها ثبت میکنند و ابزاری بینظیر برای ترکیب منطق مدیریت و جلوگیری از خرابی فراهم میکنند.
استفاده و کاربردها
دکوراتورهای کنترل استثنایی تا حد زیادی مدیریت خطای برنامه را ساده می کنند و به پنهان کردن رفتار غیرقابل اعتماد از کاربران کمک می کنند.
به عنوان مثال، یک وب سایت تجارت الکترونیک ممکن است متکی باشد روی خدمات پرداخت، موجودی و حمل و نقل برای تکمیل سفارشات. بهجای بلوکهای استثنایی پیچیده در همه جا، پردازش سفارش اصلی مانند این است place_order()
می توان برای دستیابی به انعطاف پذیری تزئین کرد.
این @handle_exceptions
دکوراتور اعمال شده در بالای آن هرگونه قطع سرویس شخص ثالث یا مشکل متناوب را در طول نهایی شدن سفارش جذب می کند. در موارد استثنا، هنگام ارائه پیام دلپذیر «سفارش انجام نشد، لطفاً بعداً دوباره امتحان کنید» به مشتری، خطاهای اشکالزدایی را ثبت میکند. این از افشای شکست پیچیده جلوگیری می کند root دلایلی مانند وقفه های پرداخت برای کاربر نهایی.
دکوراتورها بدون تغییر کد کسب و کار، مشتریان را از مشکلات خدمات غیر قابل اعتماد محافظت می کنند. آنها هنگام بروز خطا، پاسخ های پیش فرض دوستانه ای را ارائه می دهند. این باعث بهبود تجربه مشتری می شود
همچنین، دکوراتورها به توسعه دهندگان این خطاها را در پشت صحنه مشاهده می کنند. بنابراین آنها می توانند تمرکز کنند روی به طور سیستماتیک تعمیر root علل شکست این تفکیک نگرانی ها از طریق دکوراتورها پیچیدگی را کاهش می دهد. مشتریان قابلیت اطمینان بیشتری را می بینند، و شما بینش عملی در مورد عیوب دریافت می کنید – همه اینها در حالی که منطق تجاری را دست نخورده نگه می دارید.
بررسی نوع را اجرا کنید روی آرگومان های تابع
دکوراتور Enforce Type Checking با تأیید انطباق آرگومان های تابع با انواع داده های مشخص شده، جلوگیری از خطاهای مربوط به نوع، و ارتقای قابلیت اطمینان کد، یکپارچگی داده ها را تضمین می کند. به ویژه در شرایطی که پایبندی دقیق به نوع داده بسیار مهم است مفید است.
خروجی:
Result:Traceback (most recent call last):
File "C:\\\\Program Files\\\\Sublime Text 3\\\\test.py", line 36, in <module>
35
result = multiply_numbers("5", 7) # Type error: 'factor_1' must be of type 'int'
File "C:\\\\Program Files\\\\Sublime Text 3\\\\test.py", line 14, in type_checked_wrapper
raise TypeError(f"Argument '{parameter_name}' must be of type '{parameter_type.__name__}'")
TypeError: Argument 'factor_1' must be of type 'int'
این enforce_type_checking
decorator تأیید می کند که آیا آرگومان های ارسال شده به یک تابع با حاشیه نویسی نوع مشخص شده مطابقت دارند یا خیر.
درون type_checked_wrapper
، امضای تابع تزئین شده را بررسی میکند، نام پارامترها و حاشیهنویسیهای نوع را بازیابی میکند و اطمینان میدهد که آرگومانهای ارائهشده با انواع مورد انتظار همسو هستند. این شامل بررسی آرگومان های موقعیتی در برابر ترتیب آنها و آرگومان های کلیدواژه در برابر نام پارامترها می شود. اگر عدم تطابق نوع تشخیص داده شود، TypeError مطرح می شود.
این دکوراتور با کاربرد آن در multiply_numbers
تابع، که در آن آرگومان ها به صورت اعداد صحیح حاشیه نویسی می شوند. تلاش برای ارسال یک رشته منجر به یک استثنا می شود، در حالی که ارسال اعداد صحیح تابع را بدون مشکل اجرا می کند. این نوع بررسی بدون تغییر بدنه عملکرد اصلی انجام می شود.
استفاده و کاربردها
دکوراتورهای بررسی نوع برای تشخیص زودهنگام مسائل و بهبود قابلیت اطمینان استفاده می شوند. برای مثال، یک برنامه کاربردی تحت وب با تابع لایه دسترسی به داده را در نظر بگیرید get_user_data()
حاشیه نویسی برای انتظار شناسه های کاربر اعداد صحیح. اگر شناسههای رشتهای از کد فرانتاند وارد آن شوند، کوئریهای آن با شکست مواجه میشوند.
به جای اضافه کردن چک های صریح و ایجاد استثناها به صورت محلی، می توانید از این تزئین کننده استفاده کنید. اکنون هر کد بالادستی یا مصرف کننده ای که انواع نامعتبر را ارسال می کند، به طور خودکار در طول اجرای تابع ضبط می شود. دکوراتور، حاشیه نویسی ها را در مقابل انواع آرگومان ها بررسی می کند و بر این اساس قبل از رسیدن به لایه پایگاه داده، خطاها را پرتاب می کند.
این حفاظت در زمان اجرا برای اجزا از طریق دکوراتورها تضمین میکند که فقط اشکال دادههای معتبر در لایهها جریان دارند و از خطاهای مبهم جلوگیری میکنند. ایمنی نوع بدون بررسی های اضافی که منطق پاک کننده را به هم می زند تحمیل می شود.
اندازه گیری میزان استفاده از حافظه از یک تابع
هنگامی که صحبت از برنامه های کاربردی با حجم گسترده داده یا محیط های محدود به منابع می شود، Measure Memory Usage Decorator یک کارآگاه حافظه است که بینش هایی را در مورد مصرف حافظه عملکرد ارائه می دهد. این کار را با بهینه سازی استفاده از حافظه انجام می دهد.
import tracemalloc
def measure_memory_usage(target_function):
def wrapper(*args, **kwargs):
tracemalloc.start()
# Call the original function
result = target_function(*args, **kwargs)
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("lineno")
# Print the top memory-consuming lines
print(f"Memory usage of {target_function.__name__}:")
for stat in top_stats[:5]:
print(stat)
# Return the result
return result
return wrapper
# Example usage
@measure_memory_usage
def calculate_factorial_recursive(number):
if number == 0:
return 1
else:
return number * calculate_factorial_recursive(number - 1)
# Call the decorated function
result_factorial = calculate_factorial_recursive(3)
print("Factorial:", result_factorial)
خروجی:
Memory usage of calculate_factorial_recursive:
C:\\\\Program Files\\\\Sublime Text 3\\\\test.py:29: size=1552 B, count=6, average=259 B
C:\\\\Program Files\\\\Sublime Text 3\\\\test.py:8: size=896 B, count=3, average=299 B
C:\\\\Program Files\\\\Sublime Text 3\\\\test.py:10: size=416 B, count=1, average=416 B
Memory usage of calculate_factorial_recursive:
C:\\\\Program Files\\\\Sublime Text 3\\\\test.py:29: size=1552 B, count=6, average=259 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:226: size=880 B, count=3, average=293 B
C:\\\\Program Files\\\\Sublime Text 3\\\\test.py:8: size=832 B, count=2, average=416 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:173: size=800 B, count=2, average=400 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:505: size=592 B, count=2, average=296 B
Memory usage of calculate_factorial_recursive:
C:\\\\Program Files\\\\Sublime Text 3\\\\test.py:29: size=1440 B, count=4, average=360 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:535: size=1240 B, count=3, average=413 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:67: size=1216 B, count=19, average=64 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:193: size=1104 B, count=23, average=48 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:226: size=880 B, count=3, average=293 B
Memory usage of calculate_factorial_recursive:
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:558: size=1416 B, count=29, average=49 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:67: size=1408 B, count=22, average=64 B
C:\\\\Program Files\\\\Sublime Text 3\\\\test.py:29: size=1392 B, count=3, average=464 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:535: size=1240 B, count=3, average=413 B
C:\\\\Program Files\\\\Python310\\\\lib\\\\tracemalloc.py:226: size=832 B, count=2, average=416 B
Factorial: 6
این کد یک دکوراتور را نشان می دهد، measure_memory_usage
، برای اندازه گیری میزان مصرف حافظه توابع طراحی شده است.
دکوراتور، هنگامی که اعمال می شود، ردیابی حافظه را قبل از فراخوانی عملکرد اصلی آغاز می کند. هنگامی که تابع اجرای خود را کامل کرد، یک عکس فوری از حافظه گرفته می شود و 5 خط بالایی که بیشترین حافظه را مصرف می کنند چاپ می شوند.
از طریق مثال نشان داده شده است calculate_factorial_recursive()
، دکوراتور به شما امکان می دهد تا بدون تغییر در خود عملکرد، استفاده از حافظه را نظارت کنید و بینش های ارزشمندی را برای اهداف بهینه سازی ارائه می دهد.
در اصل، ابزاری ساده برای ارزیابی و تجزیه و تحلیل مصرف حافظه هر تابع در طول زمان اجرا فراهم می کند.
استفاده و کاربردها
دکوراتورهای اندازه گیری حافظه مانند اینها در توسعه برنامه برای شناسایی و عیب یابی مشکلات نفخ یا نشت حافظه بسیار ارزشمند هستند.
به عنوان مثال، یک خط لوله جریان داده با اجزای حیاتی ETL مانند transform_data()
که حجم زیادی از اطلاعات را پردازش می کند. اگرچه process در هنگام بارگیری معمولی خوب به نظر می رسد، داده های با حجم بالا مانند فروش جمعه سیاه می تواند باعث استفاده بیش از حد از حافظه و خرابی شود.
به جای اشکالزدایی دستی، تزئین پردازندههایی مانند @measure_memory_usage میتواند بینش مفیدی را نشان دهد. خواهد شد print خطوط فشرده حافظه بالا در زمان اوج جریان داده بدون تغییر کد.
شما باید به دنبال مشخص کردن مراحل خاصی باشید که به سرعت حافظه را از بین می برند و از طریق الگوریتم های بهتر یا بهینه سازی به آنها رسیدگی کنید.
چنین دکوراتورهایی به ایجاد دیدگاههای تشخیصی در مسیرهای حیاتی کمک میکنند تا روند مصرف غیرعادی را زود تشخیص دهند. به جای مشکلات تولید با تاخیر، مشکلات را می توان به طور پیشگیرانه از طریق پروفایل قبل از انتشار شناسایی کرد. آنها سردردهای اشکال زدایی را کاهش می دهند و از طریق ابزار دقیق تر برای ردیابی حافظه، خرابی های زمان اجرا را به حداقل می رسانند.
نتایج عملکرد حافظه پنهان با زمان انقضا
به طور خاص برای داده های قدیمی طراحی شده است، نتایج عملکرد حافظه پنهان با تزئین کننده زمان انقضا ابزاری است که حافظه پنهان را با یک ویژگی انقضا مبتنی بر زمان ترکیب می کند تا مطمئن شود که داده های ذخیره شده به طور منظم به روز می شوند تا از کهنگی و حفظ ارتباط جلوگیری شود.
import time
def cached_function_with_expiry(expiry_time):
def decorator(original_function):
cache = {}
def wrapper(*args, **kwargs):
key = (*args, *kwargs.items())
if key in cache:
cached_value, cached_timestamp = cache[key]
if time.time() - cached_timestamp < expiry_time:
return f"[CACHED] - {cached_value}"
result = original_function(*args, **kwargs)
cache[key] = (result, time.time())
return result
return wrapper
return decorator
# Example usage
@cached_function_with_expiry(expiry_time=5) # Cache expiry time set to 5 seconds
def calculate_product(x, y):
return f"PRODUCT - {x * y}"
# Call the decorated function multiple times
print(calculate_product(23, 5)) # Calculation is performed
print(calculate_product(23, 5)) # Result is retrieved from cache
time.sleep(5)
print(calculate_product(23, 5)) # Calculation is performed (cache expired)
خروجی:
PRODUCT - 115
[CACHED] - PRODUCT - 115
PRODUCT - 115
این کد یک دکوراتور کش را به نمایش می گذارد که دارای زمان انقضای کش خودکار است.
کارکرد cached_function_with_expiry()
یک دکوراتور تولید می کند که در صورت اعمال، از دیکشنری به نام استفاده می کند cache
برای ذخیره نتایج عملکرد و مهرهای زمانی مربوط به آنها. این wrapper()
تابع بررسی می کند که آیا نتیجه آرگومان های فعلی در حافظه پنهان است یا خیر. در صورت وجود و در مدت زمان انقضا، نتیجه ذخیره شده را برمی گرداند – در غیر این صورت، تابع را فراخوانی می کند.
مصور با استفاده از calculate_product()
، دکوراتور در ابتدا نتیجه را محاسبه و ذخیره می کند. تماسهای بعدی، نتیجه ذخیرهشده را تا پایان دوره انقضا بازیابی میکنند، در این مرحله حافظه پنهان از طریق یک محاسبه مجدد تازهسازی میشود.
در اصل، این پیاده سازی از محاسبات اضافی جلوگیری می کند در حالی که به طور خودکار نتایج را پس از دوره انقضای مشخص شده تازه می کند.
استفاده و کاربردها
دکوراتورهای انقضا کش خودکار در توسعه برنامه برای بهینه سازی عملکرد ماژول های واکشی داده ها بسیار مفید هستند.
به عنوان مثال، یک وب سایت مسافرتی را در نظر بگیرید که API پشتیبان را فراخوانی می کند get_flight_prices()
برای نمایش قیمت های زنده به کاربران در حالی که حافظه پنهان تماس با منابع داده گران قیمت پرواز را کاهش می دهد، ذخیره ثابت منجر به نمایش قیمت های قدیمی می شود.
در عوض، می توانید استفاده کنید @cached_function_with_expiry(60)
برای بازخوانی خودکار هر دقیقه. اکنون، اولین تماس کاربر، قیمتهای زنده را واکشی میکند و آنها را در حافظه پنهان میکند، در حالی که درخواستهای بعدی در یک پنجره دهه 60 به طور موثر از قیمتگذاری ذخیرهشده استفاده میکنند. اما کش ها به طور خودکار پس از مدت انقضا باطل می شوند تا داده های تازه را تضمین کنند.
این به شما امکان می دهد بدون نگرانی در مورد موارد گوشه ای مربوط به نمایش های قدیمی، جریان ها را بهینه کنید. این دکوراتور وضعیت را به طور قابل اعتماد کنترل می کند و حافظه پنهان را با تغییرات بالادستی از طریق تازه سازی قابل تنظیم هماهنگ نگه می دارد. افزونگی محاسبات مجدد صفر است، و شما همچنان بهترین اطلاعات به روز شده ممکن را برای کاربران نهایی دریافت می کنید. الگوهای ذخیره سازی رایج به راحتی برای استفاده مجدد در پایگاه کد با قوانین انقضای سفارشی بسته بندی می شوند.
نتیجه
دکوراتورهای پایتون همچنان شاهد استفاده گسترده در توسعه برنامهها برای درج شفاف نگرانیهای متقابل مشترک هستند. احراز هویت، نظارت و محدودیتها نمونههای استانداردی از موارد استفاده هستند که از تزئینات در چارچوبهایی مانند جنگو و فلاسک استفاده میکنند.
محبوبیت APIهای وب همچنین منجر به پذیرش متداول دکوراتورهای محدود کننده نرخ و حافظه پنهان برای عملکرد شده است.
دکوراتورها در واقع از زمان انتشار اولیه پایتون وجود داشته اند. گیدو ون روسوم در مقاله ای در سال 1990 در مورد بهبود با تزئینات نوشت روی پایتون. بعداً هنگامی که نحو دکوراتورهای تابع در پایتون 2.4 در سال 2004 تثبیت شد، درها را برای راه حل های ظریف از طریق برنامه نویسی گرا باز کرد. از وب تا علم داده، آنها همچنان به قدرت انتزاع و مدولار بودن در دامنه های پایتون ادامه می دهند.
نمونههای این کتابچه راهنما فقط سطح آنچه را که دکوراتورهای سفارشی میتوانند فعال کنند، خراش میدهند. مستقر روی هر هدف خاصی مانند امنیت، محدود کردن درخواست های کاربر، رمزگذاری شفاف و غیره روی، می توانید دکوراتورهای خلاقانه ای را برای رفع نیازهای خود ایجاد کنید. ساختار خطوط لوله پردازش منطقی با استفاده از ترکیبی از دکوراتورهای تخصصی تک مسئولیتی نیز استفاده مجدد بیش از افزونگی را تشویق می کند.
درک دکوراتورها نه تنها مهارت های توسعه را بهبود می بخشد، بلکه راه هایی را برای دیکته کردن رفتار برنامه به طور انعطاف پذیر باز می کند. من شما را تشویق میکنم که نیازهای مشترک را در پایههای کد خود ارزیابی کنید که میتوان آنها را به دکوراتورهای مستقل انتزاع کرد. با کمی تمرین، تشخیص نگرانی های مقطعی و گسترش کارآمد عملکردها بدون عرق کردن آسان می شود.
اگر این درس را دوست داشتید و میخواهید محتوای فناوری روشنتر از جمله خواندن پایتون، جنگو و طراحی سیستم را بررسی کنید، وبلاگ من را بررسی کنید. شما همچنین می توانید پروژه های من را با اثبات کار مشاهده کنید روی GitHub و با من ارتباط برقرار کنید روی لینکدین برای چت.
منتشر شده در 1403-01-26 22:19:09