سطح مقدماتی (Beginner Level)
ماژول contextlib در کتابخانه استاندارد پایتون (Standard Library) ابزارهایی را فراهم میکند که کار با دستور with و Context Managerها را بسیار سادهتر میکند. اگر پیش از این برای ساخت یک Context Manager مجبور بودید یک کلاس کامل با متدهای __enter__ و __exit__ بنویسید، این ماژول به شما اجازه میدهد کدهای تمیزتر و کوتاهتری بنویسید.
۱. نادیده گرفتن خطاها با suppress
یکی از رایجترین کاربردهای contextlib، متد suppress است. این متد جایگزین بلوکهای try-except-pass میشود و به شما اجازه میدهد خطاهای مشخصی را بهصورت ایمن نادیده بگیرید.
مثال اول: حذف فایل بدون بررسی وجود آن
به جای اینکه چک کنید فایل وجود دارد یا نه، یا از try-except استفاده کنید، میتوانید خطای FileNotFoundError را سرکوب (Suppress) کنید.
مثال دوم: دریافت مقدار از دیکشنری (شبیه سازی)
اگر بخواهیم عملیاتی انجام دهیم که ممکن است KeyError یا IndexError بدهد و برایمان مهم نباشد:
۲. مدیریت خروجیها با redirect_stdout
گاهی نیاز دارید خروجی printهای یک تابع یا کد را به جای کنسول، به یک فایل یا یک متغیر رشتهای هدایت کنید. redirect_stdout این کار را به سادگی انجام میدهد.
مثال اول: ذخیره پرینت در یک فایل
مثال دوم: ساکت کردن کد (Silencing)
اگر میخواهید خروجی یک کد را کلاً حذف کنید (مثلاً یک کتابخانه که پرینتهای مزاحم دارد):
import io
from contextlib import redirect_stdout
def noisy_library():
print("SYSTEM WARNING: ...")
# هدایت خروجی به ناکجا (یک بافر خالی)
with redirect_stdout(io.StringIO()):
noisy_library()
# هیچ چیزی در کنسول چاپ نمیشود
سطح پیشرفته (Professional Level)
در سطح حرفهای، contextlib قدرت واقعی خود را با دکوراتورها نشان میدهد. شما میتوانید با استفاده از Generatorها، Context Manager بسازید و نیازی به تعریف کلاس ندارید.
۱. دکوراتور contextmanager@
این دکوراتور جادویی است. به جای نوشتن کلاس با __enter__ و __exit__، شما یک تابع مینویسید که یک بار yield میکند.
- کدِ قبل از
yield: معادل__enter__ - کدِ بعد از
yield: معادل__exit__
مثال اول: ساخت تایمر ساده
مثال دوم: مدیریت کانکشن (Mock Database)
استفاده از try-finally داخل ژنراتور تضمین میکند که حتی اگر در بلاک with خطا رخ دهد، کدِ بعد از yield (بستن منابع) اجرا شود.
۲. کلاس ContextDecorator
اگر بخواهید Context Manager شما هم به صورت with و هم به صورت دکوراتور (@) قابل استفاده باشد، میتوانید از ContextDecorator ارثبری کنید. با این حال، contextmanager@ که در بالا گفتیم خودش این قابلیت را دارد!
مثال: استفاده به عنوان دکوراتور
همان تایمری که در بالا ساختیم را میتوانیم روی تابع بگذاریم:
۳. مدیریت داینامیک با ExitStack
این یکی از پیشرفتهترین ابزارهای contextlib است. زمانی که شما نمیدانید دقیقاً چند فایل یا منبع را باید باز کنید (مثلاً لیستی از فایلها دارید)، نمیتوانید از چندین with تو در تو استفاده کنید. ExitStack به شما اجازه میدهد Context Managerها را به صورت پویا و در یک پشته (Stack) مدیریت کنید.
مثال فنی: باز کردن تعداد نامشخصی فایل
فرض کنید یک لیست از نام فایلها داریم و میخواهیم همه را همزمان باز نگه داریم و در آخر همه را ببندیم.
۴. متد closing
برای اشیایی که متد close() دارند اما از پروتکل Context Manager (__enter__) پشتیبانی نمیکنند (معمولاً در کدهای قدیمی یا برخی کتابخانههای شبکه)، میتوان از closing استفاده کرد تا مطمئن شویم close() صدا زده میشود.
from contextlib import closing
from urllib.request import urlopen
# urlopen در پایتون ۳ خود Context Manager است، اما برای مثال:
with closing(urlopen("https://www.python.org")) as page:
for line in page:
print(line)
break
# page.close() is called automatically here