لو بتقرأ ملف CSV حجمه 4GB بـ open().readlines() أو pandas.read_csv()، Python بياخد 4GB من الـ RAM دفعة واحدة. لو السيرفر عنده 2GB RAM، البرنامج بيقع بـ MemoryError. الحل مش سيرفر أكبر — الحل كلمة واحدة اسمها yield.
Generators في Python: المفهوم اللي بيوفّر 98% من الذاكرة
المشكلة باختصار
Python بيتعامل مع البيانات بطريقتين: إمّا يحمّل كل شيء في الذاكرة دفعة واحدة (list, dict, set)، أو يجيبه قطعة قطعة لمّا تحتاجه (generator). الفرق بين الطريقتين مش مجرد أسلوب كتابة. هو الفرق بين برنامج بياخد 50MB وبرنامج بياخد 4GB لنفس المهمة.
أغلب المبتدئين بيكتبوا الطريقة الأولى من غير ما يعرفوا إن في طريقة تانية. فلمّا الملف يكبر، البرنامج بيقع وهم بيلوموا اللغة بدل ما يلوموا الكود.
مثال للمبتدئ: صنبور المياه vs خزان المياه
تخيّل إنك عايز تشرب كوباية مياه من بيتك. عندك خياران:
- الخيار الأول (List): تجيب خزان فيه 1000 لتر مياه، تحطه في المطبخ، وبعدين تملأ منه كوباية واحدة. الـ 999 لتر الباقيين بياخدوا مساحة ومش هتشربهم دلوقتي.
- الخيار التاني (Generator): تفتح صنبور المياه، تملأ الكوباية، وتقفل الصنبور. مفيش مياه بتتخزّن في المطبخ. لمّا تحتاج كوباية تانية، تفتح الصنبور تاني.
الـ List هي الخزان: بتجيب كل البيانات الأول، بتشغّل مكان، ولو عايز عنصر واحد بتدفع تكلفة كل العناصر. الـ Generator هو الصنبور: بيجيب عنصر واحد بس لمّا تطلبه، وبعد ما تستخدمه ينساه. ده اللي بيخلّيه يعالج ملف 4GB في 50MB ذاكرة.
التعريف العلمي الدقيق
الـ Generator هو دالة (function) في Python بترجع iterator بيولّد القيم واحدة واحدة بدل ما يرجّعها كلها مرة واحدة. الفرق التقني بين دالة عادية و generator هو كلمة yield: لمّا الـ interpreter يلاقي yield في دالة، بيحوّلها أوتوماتيكيًا لـ generator function.
الميكانيكية الداخلية: لمّا الـ generator يوصل لـ yield، بيرجّع القيمة وبيـ"يجمّد" حالة الدالة كاملة (المتغيرات، مكان الـ pointer، الـ stack frame). لمّا تطلب القيمة اللي بعدها بـ next()، بيكمّل من نفس النقطة بالظبط، مش بيبدأ من الأول. ده اللي بيخلّيه ياخد ذاكرة ثابتة بغض النظر عن حجم البيانات.
المرجع الرسمي للمفهوم ده هو PEP 255 — Simple Generators اللي اتنشر سنة 2001، والتوثيق الرسمي في Python docs.
كود شغّال على Python 3.12
السيناريو: عندك ملف CSV فيه 8.4 مليون سجل تحويلات بنكية، حجمه 3.8GB. عايز تحسب مجموع التحويلات اللي قيمتها أكبر من 10,000 جنيه.
]]>