Generators في Python للمتوسط: عالج ملف 50GB بـ 12MB رام بدون ما السيرفر يقع
مستوى المقال: متوسط — هذا الشرح يفترض إنك مرتاح مع for loops و def في Python، ومش لازم تعرف الـ Iterator Protocol مسبقاً. لو لسه بتبدأ Python خالص، احفظ المقال لما تعدي مرحلة الـ functions الأساسية.
لو سكربت Python بتاعك بيقرا ملف CSV حجمه 50 جيجا والـ OOM Killer قتله بعد 3 دقايق، المشكلة مش في الـ RAM بتاع السيرفر. المشكلة إنك بتحمّل الملف كله في الذاكرة دفعة واحدة بدل ما تقراه سطر سطر. yield واحدة بدل append+return بتنزّل الذاكرة من 47 جيجا لـ 12 ميجا على نفس السيرفر، بدون أي تعديل في الـ infrastructure أو الـ stack.
المشكلة باختصار: ليه الكود التقليدي بياكل الذاكرة كلها
الكود الطبيعي في data processing بيستخدم list لتجميع النتائج. كل عنصر بيتعمله append بيفضل في الذاكرة لحد ما الـ list كلها تترجّع. لو الملف 50 جيجا، الـ list هتاخد 50+ جيجا (Python objects فيها overhead حوالي 28 بايت لكل object صغير حسب sys.getsizeof).
اللي Generator بيعمله مختلف تماماً: بدل ما يحضّر كل النتائج ويرجّعها مرة واحدة، بيرجّع عنصر واحد بس وبيوقّف نفسه في مكانه. لما الكود اللي بيستهلكه يطلب التاني، الـ Generator بيكمل من نفس النقطة. الذاكرة المحجوزة في أي لحظة = حجم العنصر الواحد + state صغير جداً (200-300 بايت لكل generator)، مش حجم الـ collection كاملة.
قبل ما نشرح المفهوم: تخيّل المكتبة العامة
تخيّل إنك في مكتبة فيها 50 ألف كتاب وعايز تدوّر على كلمة معينة. عندك طريقتين:
- الطريقة الأولى (List): تطلب من الموظف يجيب كل الكتب على طاولتك دفعة واحدة. هتقعد ساعتين تستنّى، والطاولة هتقع من تحت الـ 50 ألف كتاب.
- الطريقة التانية (Generator): الموظف بيجيبلك كتاب واحد، تفتحه، تدوّر على الكلمة، تقول "خلّصت"، فيرجعه ويجيب اللي بعده. الطاولة عليها كتاب واحد بس في أي لحظة، والشغل بيخلص.
دلوقتي بعد ما اتضح المفهوم، نرجع للجانب العلمي بدقة: Generator في Python هو function بتستخدم yield بدل return. أول ما الـ caller يطلب القيمة التالية بـ next() أو بـ for loop، الـ function بتنفّذ لحد أول yield ثم بتتجمّد. الـ state بتاعها (المتغيرات المحلية، الـ instruction pointer، الـ call stack الخاص بيها) بيتحفظ في frame object صغير جداً.
المثال التنفيذي: قراءة CSV 50GB بـ Generator
الكود ده مأخوذ من workload حقيقي لمعالجة logs server في خدمة logistics. الملف اليومي حجمه 47 جيجا (220 مليون سطر JSON Lines).