يتيح مديرو السياق لبايثون ضمان تنظيف مورد ما، سواء نجح كود الكتلة أو فشل. يشرح مقال تقني آلية عملهم تحت تعليمة with، بالإضافة إلى عدة طرق لبنائهم واستخدامهم، تتجاوز بكثير مجرد إدارة الملفات.
يكتشف العديد من المطورين هذه الآلية عبر with open(path)، التي تغلق الملف تلقائيًا. ومع ذلك، يرى المؤلف أن الملفات هي الحالة الأقل إثارة للاهتمام: فتعليمة with تحدد بشكل أساسي كتلة يضمن الخروج منها. يعتمد البروتوكول على طريقتين، __enter__()، التي تُستدعى عند دخول الكتلة، و__exit__()، التي تُستدعى عند الخروج، سواء كان طبيعيًا، أو ناتجًا عن return، أو بسبب استثناء.
تلعب القيمة التي تُرجعها __exit__() دورًا حاسمًا. إذا كانت صحيحة (True)، يتم قمع الاستثناء، مما قد يمر دون أن يلاحظه أحد: يستمر البرنامج، ويختفي الخطأ دون أثر. أما إذا كانت False أو غير موجودة، فإن الاستثناء ينتقل لأعلى بعد عملية التنظيف. ولهذا السبب لا تُرجع مدراء السياق المذكورون في المقال أي قيمة صحيحة أبدًا.
بناء مدراء السياق الخاصة بك
تصلح الفئة عندما يحتاج مدير السياق إلى الاحتفاظ بحالة يمكن للمستدعي قراءتها. يأخذ المقال مثالًا على مؤقت زمني، يقيس مدة تنفيذ كتلة باستخدام time.perf_counter() ويعرض النتيجة حتى لو فشلت الكتلة. وبالتالي، تعرض عملية استخراج ميزات فاشلة «feature extraction: 12.418s (failed)»، قبل أن ينتقل الخطأ لأعلى.
لمهمة خطية، أي إعداد، ثم كتلة، ثم استعادة، يسمح الديكوراتور @contextmanager بكتابة دالة مولدة بسيطة. كل ما يسبق yield يُنفذ عند الدخول، وكل ما يليه يُنفذ عند الخروج. يوصي المقال بوضع عملية الاستعادة داخل finally، وعدم ابتلاع الاستثناء مطلقًا داخل try/except دون إعادة إثارته. يجب أن تُنتج yield قيمة واحدة فقط، وإلا سيتم رفع خطأ RuntimeError.
استعادة الحالة وإدارة موارد متعددة
توفر المكتبة القياسية بالفعل عدة مدراء سياق. TemporaryDirectory() ينشئ دليلاً باسم فريد ويحذفه في نهاية الكتلة، مما يتجنب بقاء ملفات متبقية من تنفيذ سابق. منذ بايثون 3.12، تتيح المعلمة delete=False الاحتفاظ بالملفات لتحليل لاحق. في نظام ويندوز، قد يؤدي فشل حذف ملف مفتوح؛ وتخفف المعلمة ignore_cleanup_errors=True، المتاحة منذ بايثون 3.10، من هذه المشكلة دون حلها.
للتعديلات على القواميس، يقوم patch.dict()، المأخوذ من وحدة unittest.mock، باستعادة الحالة الأصلية بعد الكتلة: تستعيد المفتاح الموجود قيمته، ويتم حذف المفتاح غير الموجود. ومع ذلك، يشير المقال إلى أن os.environ عام على مستوى العملية. وبالتالي، ترى جميع المهام والخيوط التعديل، مما يجعله محفوفًا بالمخاطر في الكود المتزامن.
عندما لا يُعرف عدد الموارد إلا أثناء التنفيذ، مثل عدد الملفات المراد دمجها، يقوم ExitStack بتجميع مدراء السياق. تسجل طريقته enter_context() مدير سياق، وتسجل callback() دالة بسيطة. عند الخروج، تُزال العناصر من المكدس بترتيب عكسي. يوضح المقال أن هذا الإزالة من المكدس يحدث في نهاية الكتلة أو عند استدعاء close()، وليس عند حذف الكائن بواسطة جامع القمامة. قد تكون الفئة المخصصة مفضلة عندما يصبح المكدس ثقيلًا جدًا.




