يظهر IndexError أحيانًا داخل حلقة for أو while رغم أن القائمة كانت صحيحة في بداية التنفيذ. في هذه الصفحة من سلسلة مشكلة وحل سنركز على الحالات التي تسبب الخطأ بسبب range() أو pop() أو تغيّر طول List أثناء الحلقة.
أما إذا كان الخطأ لديك مجرد وصول مباشر إلى فهرس غير موجود مثل items[10]، أو تريد شرح الفهرسة من الصفر وlen() وآخر index، فراجع المقال المرجعي: حل خطأ IndexError: list index out of range في بايثون. الهدف هنا مختلف: تشخيص أخطاء IndexError الناتجة عن منطق الحلقة وتغيّر القائمة أثناء التنفيذ.
IndexError: list index out of range
{alertInfo} الحل المختصر: داخل الحلقات افحص ثلاثة أشياء أولًا: هل كتبتrange(len(items) + 1)أو شرطًا مثلindex <= len(items)؟ هل تحذف عناصر باستخدامpop()أوremove()أثناء التكرار؟ وهل تحتاج إلى index أصلًا أم يمكن استخدامfor item in itemsأوenumerate(items)؟
{getToc} $title={محتوى المقال}
متى يكون IndexError ناتجًا عن الحلقة نفسها؟
يكون الخطأ مرتبطًا بالحلقة عندما تبدأ الفهارس صحيحة ثم تصبح غير صالحة بسبب حدود التكرار أو بسبب تغيّر القائمة أثناء التنفيذ. أكثر الأنماط التي يجب البحث عنها هي range()، ومتغير index داخل while، واستدعاءات pop() أو remove() أو del داخل الحلقة.
القاعدة المهمة: الحلقة قد تستمر في إنتاج أرقام فهارس محسوبة قبل أن تتغير القائمة. لذلك لا يكفي أن تقول «استخدمت len() إذن الكود آمن»؛ يجب أن تتأكد أن طول القائمة لا يتغير بطريقة تجعل الفهرس الحالي خارج النطاق.
قاعدتان فقط قبل تشخيص أخطاء الحلقات
لن نكرر شرح الفهرسة العام الموجود في المقال المرجعي. احتفظ فقط بهاتين القاعدتين أثناء Debugging:
- إذا كان طول القائمة
n، فآخر index موجب صالح هوn - 1. len(items)تعطي عدد العناصر، وليست فهرسًا صالحًا لعنصر جديد.
لهذا السبب فإن فهرسًا يساوي len(items) يكون خارج القائمة. هذه القاعدة تصبح مهمة جدًا عند كتابة حدود range() أو شروط while.
السبب الأول: range(len(list) + 1) يتجاوز آخر index
هذا من أوضح أسباب IndexError داخل for loop. إذا كانت القائمة تحتوي ثلاثة عناصر فإن range(len(items)) تنتج 0, 1, 2، وهي الفهارس الصحيحة. إضافة + 1 تجعل الحلقة تصل إلى index غير موجود.
items = ["A", "B", "C"]
for index in range(len(items) + 1):
print(items[index])
في الدورة الأخيرة تصبح قيمة index مساوية لـ3، بينما آخر فهرس صالح هو 2، فيظهر:
IndexError: list index out of range
التصحيح المباشر
items = ["A", "B", "C"]
for index in range(len(items)):
print(items[index])
لكن إذا كنت لا تحتاج إلى رقم الفهرس، فالأوضح المرور على العناصر نفسها:
for item in items:
print(item)
الحل الأفضل عند الحاجة إلى index والقيمة: enumerate()
إذا كان هدفك الحصول على رقم العنصر وقيمته معًا، فاستخدم enumerate() بدل إدارة الفهارس يدويًا. هذا يقلل فرص كتابة حدود خاطئة مثل len(items) + 1.
items = ["A", "B", "C"]
for index, item in enumerate(items):
print(index, item)
الناتج:
0 A
1 B
2 C
وإذا كان الرقم للعرض للمستخدم فقط وتريده أن يبدأ من 1، استخدم start=1، لكن لا تستخدم هذا الرقم مباشرة كفهرس للقائمة:
for number, item in enumerate(items, start=1):
print(number, item)
السبب الثاني: range(len(list)) قد يفشل إذا تغيّر طول القائمة
range(len(numbers)) صحيح عندما يبقى طول القائمة ثابتًا. المشكلة تبدأ إذا حذفت عناصر أثناء الحلقة. قيمة len(numbers) تُستخدم عند إنشاء range قبل بدء التكرار، ثم قد تصبح القائمة أقصر بينما تستمر الحلقة في إعطاء الفهارس القديمة.
numbers = [10, 20, 30, 40]
for index in range(len(numbers)):
if numbers[index] > 20:
numbers.pop(index)
عندما تُحذف قيمة 30 تقصر القائمة وتتحرك العناصر. بعدها قد تصل الحلقة إلى index كان صالحًا قبل الحذف ولم يعد صالحًا الآن، فتظهر IndexError.
حل آمن وواضح: أنشئ قائمة جديدة
numbers = [10, 20, 30, 40]
filtered_numbers = []
for number in numbers:
if number <= 20:
filtered_numbers.append(number)
print(filtered_numbers)
ويمكن كتابة الفكرة نفسها بـList Comprehension:
filtered_numbers = [
number
for number in numbers
if number <= 20
]
{alertWarning} إذا كنت تغيّر طول القائمة التي تعتمد الحلقة على فهارسها، فتعامل مع ذلك كإشارة خطر. غالبًا يكون إنشاء قائمة جديدة أو فصل مرحلة الحذف عن مرحلة التكرار أوضح وأقل عرضة للأخطاء.
الحذف أثناء for loop: لماذا قد تحصل على خطأ أو نتائج ناقصة؟
عند استخدام pop(index) مع حلقة تعتمد على الفهارس قد تحصل على IndexError لأن طول القائمة يتغير. أما عند المرور على العناصر مباشرة وحذفها بـremove() فقد لا يظهر خطأ، لكن قد يتم تخطي بعض العناصر بسبب تحرك القائمة أثناء التكرار.
numbers = [1, 2, 3, 4, 5, 6]
for number in numbers:
if number % 2 == 0:
numbers.remove(number)
print(numbers)
لا تعتمد على هذا النمط إذا كان هدفك تصفية القائمة. استخدم قائمة جديدة أو List Comprehension:
numbers = [1, 2, 3, 4, 5, 6]
numbers = [
number
for number in numbers
if number % 2 != 0
]
print(numbers)
السبب الثالث: pop() على قائمة فارغة
الخطأ هنا من عائلة IndexError لكنه يأتي برسالة مختلفة. إذا كانت القائمة فارغة ثم استدعيت pop() بدون عنصر متاح، فستحصل على:
cart = []
cart.pop()
IndexError: pop from empty list
الحل هو التأكد من وجود عنصر قبل الحذف:
cart = []
if cart:
last_item = cart.pop()
print(last_item)
else:
print("السلة فارغة.")
وبالمثل، pop(index) يرفع IndexError إذا كان index خارج نطاق القائمة، حتى لو لم تكن القائمة فارغة.
السبب الرابع: شرط while يسمح بـ index يساوي len(list)
في حلقات while يظهر الخطأ غالبًا بسبب استخدام <= بدل <. المثال التالي يبدو قريبًا من الصحيح لكنه يسمح بدورة إضافية:
items = ["A", "B", "C"]
index = 0
while index <= len(items):
print(items[index])
index += 1
عندما يصبح index == 3 يبقى الشرط 3 <= 3 صحيحًا، لكن items[3] غير موجود.
التصحيح
items = ["A", "B", "C"]
index = 0
while index < len(items):
print(items[index])
index += 1
إذا كنت تتعلم الفرق بين for وwhile وحدود range()، راجع أيضًا شرح الحلقات for وwhile وrange في بايثون.
كيف تشخّص IndexError داخل loop خطوة بخطوة؟
بدل تغيير الفهرس عشوائيًا حتى يختفي الخطأ، اطبع القيم التي تتحكم في الحلقة قبل السطر الذي يفشل:
print("index =", index)
print("len(items) =", len(items))
print("items =", items)
print(items[index])
- حدد السطر الذي يحتوي على
items[index]أوpop(index). - اطبع قيمة
indexوطول القائمة في الدورة التي ينهار فيها البرنامج. - ابحث عن
+ 1داخلrange()أو شرط<= len(items). - ابحث عن
pop()وremove()وdelداخل الحلقة. - تحقق هل طول القائمة يتغير بينما الحلقة ما زالت تستخدم فهارس قديمة.
- إذا كنت تحتاج العنصر فقط، استخدم
for item in items. وإذا تحتاج الرقم والعنصر، استخدمenumerate().
جدول سريع: سبب IndexError داخل الحلقة والحل
| النمط | لماذا يفشل؟ | الحل الأنسب |
|---|---|---|
range(len(items) + 1) | ينتج index إضافيًا خارج القائمة. | range(len(items)) أو المرور المباشر. |
while index <= len(items) | يسمح بـindex يساوي طول القائمة. | استخدم index < len(items). |
pop(index) أثناء حلقة فهارس | طول القائمة يتغير بينما الفهارس مستمرة. | أنشئ قائمة جديدة أو أعد تصميم الحذف. |
pop() والقائمة فارغة | لا يوجد عنصر لإزالته. | تحقق بـif items: أولًا. |
| تحتاج index والقيمة معًا | إدارة الفهارس يدويًا تزيد فرص الخطأ. | استخدم enumerate(). |
متى أرجع إلى شرح IndexError العام؟
إذا لم يكن الخطأ مرتبطًا بحلقة أو بتغيّر طول القائمة، فارجع إلى المقال الأساسي المذكور في مقدمة الصفحة؛ فهو المخصص لشرح الفهرسة، وlen()، والفهرس السالب، والوصول المباشر إلى عنصر غير موجود.
ويمكنك متابعة بقية الأخطاء من سلسلة مشكلة وحل في بايثون.
مصادر رسمية للتوسع
الخلاصة
- هذه الصفحة مخصصة لـ
IndexErrorالذي يظهر داخلforأوwhileأو بعد تغيّر طول القائمة. - لا تستخدم
range(len(items) + 1)للوصول إلى عناصر القائمة. range(len(items))قد يصبح خطرًا إذا حذفت عناصر وغيّرت طول القائمة أثناء الحلقة.- استخدم
enumerate()عندما تحتاج index والقيمة معًا. - لا تستدعِ
pop()على قائمة فارغة، ولا تعتمد على فهارس قديمة بعد الحذف. - عند تصفية قائمة، إنشاء قائمة جديدة غالبًا أوضح وأكثر أمانًا من تغيير القائمة نفسها أثناء التكرار.
{alertSuccess} قاعدة عملية: إذا ظهرIndexErrorداخل loop، اطبعindexوlen(items)واسأل فورًا: هل حدود الحلقة أكبر من القائمة؟ وهل تغيّر طول القائمة منذ أن بدأت الحلقة؟
أسئلة شائعة
لماذا يظهر list index out of range داخل for loop؟
غالبًا لأن الحلقة تنتج index غير موجود، مثل استخدام range(len(items) + 1)، أو لأن القائمة أصبحت أقصر بسبب حذف عناصر أثناء التكرار.
هل range(len(list)) يسبب IndexError؟
ليس بحد ذاته إذا بقي طول القائمة ثابتًا. لكنه قد يؤدي إلى IndexError إذا غيّرت طول القائمة داخل الحلقة ثم استمرت الحلقة في استخدام الفهارس المبنية على الطول السابق.
كيف أتجنب IndexError عند حذف عناصر داخل loop؟
لا تربط حذف العناصر بفهرس متزايد على قائمة يتغير طولها. عند التصفية استخدم قائمة جديدة أو List Comprehension، أو افصل مرحلة تحديد العناصر عن مرحلة الحذف.
ما معنى IndexError: pop from empty list؟
يعني أنك استدعيت pop() بينما القائمة لا تحتوي على أي عنصر. تحقق من القائمة باستخدام if items: قبل الحذف عندما تكون فارغة احتمالًا طبيعيًا.
هل enumerate يمنع IndexError دائمًا؟
لا، لكنه يقلل الحاجة إلى إدارة الفهارس يدويًا عندما تحتاج index والقيمة معًا. إذا عدلت القائمة نفسها أثناء التكرار، فقد تظهر مشاكل منطقية أخرى، لذلك لا تعتبره بديلًا عن تصميم الحلقة بشكل صحيح.
ما الفرق بين هذا المقال والمقال الأساسي عن IndexError؟
هذا المقال يركز على أخطاء الحلقات وrange() وpop() وتغيّر طول القائمة. أما المقال الأساسي المشار إليه في المقدمة فيشرح معنى list index out of range والفهرسة وحدود القائمة بشكل عام.



