Python: المكرّرات والمولّدات
آخر تحديث: 2026-09-22
لقد كنت تستخدم
for x in list:لتكرار البيانات، لكن هل فكرت يوماً في ما يمكن أن يتبعin؟ لماذا لا يستهلكrange(1000000)ذاكرة؟ المكرّرات و المولّدات هي الإجابة. تتيح لك التعامل مع تدفقات بيانات "لا نهائية" — تُنتَج عند الطلب، لا تُخزّن في الذاكرة.
1. ما هو بروتوكول المكرّر
في بايثون، أي كائن يمكن استخدامه في حلقة for هو قابل للتكرار. إنها تنفذ بروتوكول المكرّر:
▶ مثال: أساسيات بروتوكول المكرّر
▶ جرّب الكودالناتج:
# Executed successfully
حلقة for تفعل هذا أساساً:
▶ مثال: محاكاة حلقة for يدوياً
▶ جرّب الكودالناتج:
# Executed successfully
2. مكرّرات مخصصة
نفّذ __iter__ و __next__ لجعل فصلك يعمل مع حلقات for:
▶ مثال: مكرّر العد التنازلي
▶ جرّب الكودالناتج:
# Function defined successfully
▶ مثال: مكرّر فيبوناتشي (الصعوبة ⭐⭐)
▶ جرّب الكودالناتج:
# Function defined successfully
3. المولّدات: yield
كتابة فئة بـ __iter__ و __next__ مملة. دوال المولّدات تستخدم yield لتحقيق نفس الشيء:
▶ مثال: مولّد العد التنازلي
▶ جرّب الكودالناتج:
# Function defined successfully
yield يختلف عن return — return ينهي الدالة، بينما yield يُوقفها مؤقتاً، ويتذكر الحالة الحالية، ويستأنف من حيث توقف في الاستدعاء التالي.
▶ مثال: مولّد فيبوناتشي
▶ جرّب الكودالناتج:
# Function defined successfully
4. التحميل الكسول مع المولّدات
أكبر ميزة للمولّدات — لا تُنشئ جميع البيانات مرة واحدة، بل عند الطلب:
▶ مثال: التحميل الكسول للمولّد
▶ جرّب الكودالناتج:
Generating 1
Generating 2
Generating 3
Generator created, but not yet executed
هل ترى تدفق التنفيذ؟ كل استدعاء لـ next() ينفذ حتى yield التالي، ثم يتوقف. هذا مفيد بشكل لا يصدق للبيانات الكبيرة:
▶ مثال: قراءة ملف كبير سطراً بسطر
# طريقة "كسولة" لمعالجة الملفات الكبيرة
def read_large_file(filename):
"""اقرأ ملفاً كبيراً سطراً بسطر، دون تحميله كله في الذاكرة"""
with open(filename, "r", encoding="utf-8") as f:
for line in f:
yield line.strip()
# معالجة بالمولّد — سطر واحد فقط في الذاكرة في كل مرة
for line in read_large_file("huge_log.txt"):
if "ERROR" in line:
print(line) # السرعة محدودة فقط بسرعة قراءة القرص
الناتج:
// Executed successfully
5. تعبيرات المولّدات
مشابهة لشاملات القوائم، لكن بـ أقواس — تعبيرات المولّدات تُقيّم بتكاسل:
▶ مثال: تعبير مولّد
▶ جرّب الكودالناتج:
# Executed successfully
| المقارنة | شاملة قائمة [] |
تعبير مولّد () |
|---|---|---|
| الذاكرة | كل البيانات في الذاكرة | عند الطلب، لا شيء تقريباً |
| السرعة | توليد ووصول سريعان | كسول، يحسب في كل مرة |
| قابلية إعادة الاستخدام | يمكن التكرار عدة مرات | يمكن التكرار مرة واحدة فقط |
| حالة الاستخدام | بيانات صغيرة، استخدام متكرر | بيانات كبيرة، مرور واحد |
6. حالات الاستخدام الشائعة
- معالجة البيانات الكبيرة: اقرأ ملفات سجلات/CSV كبيرة سطراً بسطر، متجنباً نفاد الذاكرة.
- التسلسلات اللانهائية: يمكن للمولّدات تمثيل تدفقات بيانات "لا نهائية" — فيبوناتشي، بيانات الاستشعار.
- معالجة خط الأنابيب: اربط عدة مولّدات في خط أنابيب معالجة بيانات.
- التكرار العكسي:
reversed()يُرجع مكرّراً لبعض الأنواع. - التقييم الكسول: الدوال تُرجع مولّداً؛ المتصل يقرر عدد القيم التي سيأخذها.
❓ أسئلة شائعة
return لإرجاع نتيجة مرة واحدة وتنتهي. المولّد يستخدم yield لإنتاج القيم واحدة تلو الأخرى، ويتوقف ويحفظ الحالة بعد كل yield. كل استدعاء لدالة مولّد يُرجع كائن مولّد جديد.list(gen) لتحويل المولّد إلى قائمة. لكن إذا كانت البيانات كبيرة جداً، التحويل إلى قائمة سيستهلك الذاكرة. حل وسط: استخدم itertools.tee() لنسخ المولّد، أو استدع دالة المولّد مرة أخرى لإنشاء واحد جديد.yield و return؟return في المولّد يوقف التكرار فعلياً ويرفع StopIteration بقيمة اختيارية. لكن نادراً ما يُستخدم. إذا وجدت دالة تحتاج كلاً من yield و return، أعد النظر في التصميم.📖 ملخص
- الكائنات القابلة للتكرار تدعم حلقات
for؛ في الخلفية، تنفذ بروتوكول__iter__و__next__ - التكرار اليدوي:
iter()يحصل على مكرّر،next()يحصل على القيمة التالية،StopIterationينهيه - دوال المولّدات تستخدم
yieldلإنتاج القيم واحدة تلو الأخرى، وحفظ الحالة تلقائياً - المولّدات كسولة — تُنتج بقدر الحاجة، بدون هدر في الذاكرة
- تعبيرات المولّدات
(x for x in seq)مثل شاملات القوائم، لكن تُقيّم بتكاسل yield fromيُفوّض لمولّد آخر (متقدم، تعلمه عندما تحتاجه)
📝 تمارين
-
أساسي (الصعوبة ⭐): اكتب مولّداً
even_numbers(max_n)يُنشئ جميع الأرقام الزوجية من 0 إلىmax_n. -
متوسط (الصعوبة ⭐⭐): اكتب مولّداً
repeat_list(items, times)يُكرّر كل عنصر من قائمةtimesمرة. مثال:list(repeat_list([1, 2, 3], 2))يُرجع[1, 1, 2, 2, 3, 3]. -
تحدي (الصعوبة ⭐⭐⭐): اكتب مولّداً
deep_flatten(nested)يُسطح القوائم المتداخلة بشكل تعسفي. مثال:list(deep_flatten([1, [2, [3, 4]], 5]))يُرجع[1, 2, 3, 4, 5]. تلميح: كرّر على العناصر؛ إذا كان العنصر قائمة، استخدمyield fromللعودية منه.