سطح مقدماتی (Beginner Level)
مدیریت منابع (Resource Management) یکی از مهمترین بخشهای برنامهنویسی است. وقتی با فایلها، دیتابیسها یا شبکه کار میکنیم، باید مطمئن شویم که منابع پس از استفاده آزاد میشوند. در پایتون، دستور with و مفهوم Context Manager این کار را به صورت خودکار و ایمن انجام میدهند.
۱. دستور with و چرایی استفاده از آن
در حالت عادی، اگر فایلی را باز کنید و فراموش کنید آن را ببندید (یا اروری رخ دهد که مانع بستن شود)، حافظه اشغال میماند. دستور with تضمین میکند که حتی در صورت بروز خطا، عملیات "پاکسازی" (مثل بستن فایل) انجام شود.
مثال اول: مقایسه روش سنتی و روش with
در این مثال میبینیم که روش قدیمی چقدر طولانی و مستعد خطاست.
# Static: Traditional way (Not recommended)
# We have to manually close the file, even if an error occurs.
file = open("example.txt", "w")
try:
file.write("Hello World")
finally:
file.close() # Must manually close
مثال دوم: استفاده از with (روش استاندارد)
این کد همان کار بالا را انجام میدهد اما بسیار تمیزتر است. پایتون به صورت خودکار فایل را میبندد.
مثال سوم: مدیریت چندین منبع همزمان
شما میتوانید چندین کانتکست را در یک دستور with مدیریت کنید (از پایتون ۳.۱۰ به بعد میتوان از پرانتز استفاده کرد).
۲. کاربرد with در قفلها (Locks)
علاوه بر فایلها، with در برنامهنویسی چندنخی (Threading) برای مدیریت قفلها بسیار کاربرد دارد تا از deadlock جلوگیری شود.
مثال اول: استفاده از Lock
سطح پیشرفته (Professional Level)
در این سطح یاد میگیریم که with واقعاً چطور کار میکند (پروتکل Context Manager) و چگونه کلاسها و توابع خودمان را با قابلیت استفاده در with بسازیم.
۱. پروتکل Context Management (__enter__ و __exit__)
هر کلاسی که دارای دو متد جادویی __enter__ و __exit__ باشد، یک Context Manager است.
__enter__: وقتی وارد بلوکwithمیشویم اجرا میشود. خروجی آن در متغیر جلویasقرار میگیرد.__exit__: وقتی بلوک تمام شود (یا ارور رخ دهد) اجرا میشود.
مثال اول: ساخت یک Context Manager ساده
یک کلاس میسازیم که ورود و خروج به بلوک را لاگ میکند.
مثال دوم: مدیریت خطا در __exit__
اگر در بلوک with خطایی رخ دهد، اطلاعات آن به __exit__ پاس داده میشود. اگر __exit__ مقدار True برگرداند، خطا سرکوب (Suppress) میشود؛ در غیر این صورت برنامه کرش میکند.
۲. ماژول contextlib و دکوراتور @contextmanager
نوشتن کلاس کامل برای کارهای ساده ممکن است سربار داشته باشد. ماژول contextlib اجازه میدهد با استفاده از Generator ها، Context Manager بسازیم.
مثال اول: تبدیل تابع به Context Manager
از yield برای جدا کردن کدهای قبل از ورود (__enter__) و بعد از خروج (__exit__) استفاده میشود.
مثال دوم: contextlib.suppress
یک ابزار آماده برای نادیده گرفتن خطاهای خاص بدون نیاز به بلوک try-except طولانی.
مثال سوم: contextlib.closing
برای اشیایی که متد close() دارند اما پروتکل Context Manager را پیادهسازی نکردهاند (مثل برخی کانکشنهای قدیمی یا urllib) استفاده میشود.
# Static: Using closing for objects lacking context support
from contextlib import closing
from urllib.request import urlopen
# urlopen in older python versions might not support 'with' directly
# closing() calls .close() automatically upon exit
with closing(urlopen("https://www.homeofpython.com")) as page:
content = page.read()