Context Managers في Python: اقفل الموارد حتى وقت الخطأ
هتعرف هنا إزاي تستخدم with في Python عشان تضمن إن الملفات والاتصالات والـ locks تتقفل حتى لو الكود وقع في النص.
مستوى القارئ: متوسط
المشكلة باختصار
الطريقة الشائعة الغلط إنك تفتح ملف أو connection وتفترض إن آخر سطر في الدالة هيقفل كل حاجة. الطريقة دي بتفشل أول ما يحصل exception قبل close(). النتيجة: file handles مفتوحة، locks مش متحررة، واتصالات قاعدة بيانات بتتراكم لحد ما التطبيق يبدأ يبطأ أو يرفض طلبات جديدة.
الافتراض إن عندك سكربت أو API صغير بيتعامل مع ملفات CSV أو تقارير أو اتصالات قصيرة بقاعدة بيانات. مش لازم تكون بتدير نظام ضخم عشان المشكلة تظهر. في Windows أو Linux، ألف loop فاشل ممكن يسيب مئات الموارد معلقة لو التنظيف مش مضمون.
مثال بسيط: الباب لازم يتقفل حتى لو حصل خطأ
ركز في المثال ده. عندك موظف بيفتح غرفة الأرشيف، ياخد ملف، وبعدها لازم يقفل الباب. لو التليفون رن أو الكهرباء قطعت، الباب برضه لازم يتقفل. الـ Context Manager بيعمل نفس الفكرة بالظبط: يفتح المورد في البداية، وينفذ شغلك، ثم ينادي جزء التنظيف حتى لو حصل خطأ.
علميًا، جملة with تستدعي __enter__() عند الدخول، وتستدعي __exit__() عند الخروج. __exit__() بتستقبل معلومات الخطأ لو حصل. تقدر تسيبه يظهر للمستدعي، أو تبتلعه في حالات ضيقة جدًا. الأفضل غالبًا إنك تنظف المورد وتسيب الخطأ يطلع.
الكود اللي يسبب المشكلة
المثال التالي متعمد يكون سيئًا. لو حصل خطأ قبل close()، الملف يفضل مفتوح لحد ما garbage collector يتصرف، وده مش ضمان هندسي تعتمد عليه.
def write_report_bad(path, rows):
f = open(path, "w", encoding="utf-8")
for row in rows:
if row == "BROKEN":
raise ValueError("invalid row")
f.write(row + "\n")
f.close()
الكود ده ممكن يعدي في الاختبار لو البيانات سليمة. اللي بيحصل فعلاً في الإنتاج إن صف واحد بايظ يوقف الدالة قبل close(). لو السكربت بيتكرر 1000 مرة، هتبدأ تشوف مشاكل مثل Too many open files أو lock على ملف مش عارف تمسحه.
أفضل طريقة: استخدم with
نفس المنطق، لكن التنظيف بقى جزء من هيكل اللغة نفسه. ده مش مجرد شكل أجمل للكود. ده عقد واضح: افتح المورد، نفذ الشغل، نظف بعد الخروج.