أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالمناهج والباقات
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
البرمجة بالعربي

الـ Generators في بايثون: اقرأ ملفًا بحجم 10 جيجابايت بذاكرة لا تتعدّى ميجابايت واحد

متوسط٢٨ يونيو ٢٠٢٦4 دقائق قراءة
الـ Generators في بايثون: اقرأ ملفًا بحجم 10 جيجابايت بذاكرة لا تتعدّى ميجابايت واحد

الـ Generators في بايثون: اقرأ ملفًا بحجم 10 جيجابايت بذاكرة لا تتعدّى ميجابايت واحد

مستوى المقال: متوسط. الافتراض إنك بتكتب دوال وحلقات في بايثون، ومرتاح تشغّل سكربت من التيرمنال. مش لازم تكون خبير.

لو عندك ملف لوج بحجم عدة جيجابايت وحاولت تفتحه بـ readlines()، البرنامج هيحاول يحمّله كله في الرام وغالبًا هيقع بـ MemoryError. الحل مش سيرفر أكبر — الحل سطر واحد: اقرأ الملف كـ generator. هتمشي على الملف سطر سطر، والذاكرة تفضل ثابتة مهما كبر الملف.

المشكلة باختصار

أغلب الكود اللي بيتعامل مع البيانات بيعمل غلطة واحدة: بيحمّل كل حاجة في الذاكرة قبل ما يبدأ يشتغل. f.readlines() بترجّع list فيها كل أسطر الملف. ملف 1 جيجا بيتحوّل لقائمة بتاكل أكتر من 1 جيجا من الرام، لأن كل سطر بيتخزّن ككائن str مستقل بـ overhead.

الـ generator بيقلب المعادلة. بدل ما يجهّز كل النتائج دفعة واحدة، بيجهّز نتيجة واحدة، يسلّمهالك، يستنى، وبعدين يجهّز اللي بعدها لما تطلب. ده اسمه التقييم الكسول (lazy evaluation).

مقارنة استهلاك الذاكرة بين readlines والـ generator

قبل المفهوم: مثال أمين المخزن

تخيّل إنك طلبت من أمين مخزن جرد بـ 10 ملايين صنف. الطريقة الأولى: يحطّ القائمة كاملة على مكتبك، فالرام تتملي وممكن تقع. ده الـ list. الطريقة التانية: كل ما تقوله هات اللي بعده، يجيبلك صنف واحد بس. ده الـ generator: ورقة واحدة على المكتب في كل لحظة مهما كبر المخزن. النتيجة واحدة، لكن في التانية ماحتجتش مساحة تخزّن الكل مرة واحدة.

الـ Generator علميًا

الـ generator دالة فيها yield بدل return. نداء الدالة مابيشغّلش جسمها — بيرجّع كائن generator. كل مرة تطلب القيمة اللي بعدها بحلقة for أو بـ next()، الدالة بتشتغل لحد أول yield، بتسلّم القيمة، وبتتجمّد بكل متغيّراتها. لما تطلب تاني بتكمّل من حيث وقفت. فهي بتخزّن مكان الوقوف بس، فالذاكرة شبه ثابتة O(1) مقابل O(n) في الـ list.

الكود: اقرأ ملفًا عملاقًا سطرًا سطرًا

Python
def read_lines(path):
    with open(path, encoding="utf-8") as f:
        for line in f:
            yield line.rstrip("\n")

total = 0
for line in read_lines("access.log"):
    total += len(line)

print(total)

كائن الملف في بايثون هو نفسه iterator كسول: بيقرأ سطر واحد في كل لفة، مش الملف كله.

الأرقام اللي قِستها بنفسي

عملت ملف 555 ميجابايت (6 ملايين سطر)، وقِست ذاكرة الذروة بـ tracemalloc على بايثون 3.11:

Python
import tracemalloc

def with_generator(path):
    def reader(p):
        with open(p) as f:
            for ln in f:
                yield ln
    return sum(len(ln) for ln in reader(path))

def with_readlines(path):
    with open(path) as f:
        lines = f.readlines()
    return sum(len(ln) for ln in lines)

for name, fn in [("generator", with_generator), ("readlines", with_readlines)]:
    tracemalloc.start()
    fn("big.log")
    _, peak = tracemalloc.get_traced_memory()
    tracemalloc.stop()
    print(name, round(peak / 1024 / 1024, 2), "MB")
  • readlines(): ذروة 883 ميجابايت، أكبر من حجم الملف (1.6×) بسبب overhead كائنات str.
  • generator: ذروة 22 كيلوبايت فقط، وثابتة.

الفرق حوالي 40,000 مرة، والناتج واحد. ده الفرق بين سكربت بيشتغل وسكربت بيقع على ملف كبير.

نفس الفكرة بسطر واحد: generator expression

Python
squares_list = [x * x for x in range(10_000_000)]   # list يتبني كله في الرام
squares_gen  = (x * x for x in range(10_000_000))   # generator كسول

total = sum(len(line) for line in open("access.log", encoding="utf-8"))

الـ trade-offs اللي لازم تعرفها

  • بتكسب: ذاكرة شبه ثابتة وبداية تنفيذ أسرع.
  • بتخسر: الـ generator يُستهلك مرة واحدة؛ للمرور تاني اعمل واحد جديد أو استخدم list.
  • مفيش فهرسة ولا طول: مايصحّش gen[5] ولا len(gen).

متى لا تستخدم الـ Generators

لو البيانات صغيرة وبتدخل الرام، أو محتاج تمرّ عليها أكتر من مرة، أو محتاج فهرسة وطول، أو مكتبة بتطلب list صريحة — الـ list أنسب. الـ generator للبيانات الكبيرة أو المتدفّقة أو اللي حجمها مش معروف.

الخطوة التالية

دوّر على أي .readlines() أو list(...) بيلفّ ملف أو query كبيرة، وحوّلها لـ for x in source مباشرة. شغّل سكربت القياس فوق على ملف حقيقي وقارن ذروة الذاكرة قبل وبعد.

المصادر

  • توثيق بايثون — تعريف Generator: docs.python.org/3/glossary
  • PEP 255 — Simple Generators: peps.python.org/pep-0255
  • PEP 289 — Generator Expressions: peps.python.org/pep-0289
  • توثيق بايثون — tracemalloc: docs.python.org/3/library/tracemalloc

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة