خطآن يتصدّران شاشة كل متعلّم بايثون في أسابيعه الأولى:
IndexError: list index out of range
KeyError: 'email'
كلاهما نفس الفكرة بوجهين: طلبت شيئًا من مكان غير موجود — فهرسًا خارج القائمة، أو مفتاحًا خارج القاموس. لنفهم كل واحد ونتقن الوصول الآمن.
أولًا: IndexError — حدود القائمة
القاعدة الذهبية: العدّ من صفر
tracks = ["HTML", "CSS", "JavaScript"]
# 0 1 2
print(tracks[2]) # ✅ JavaScript — آخر عنصر
print(tracks[3]) # 💥 IndexError: list index out of range
القائمة طولها 3 لكن آخر فهرس صالح هو 2 (len - 1). طلبُ tracks[len(tracks)]
هو أشهر زلة "off-by-one" في تاريخ البرمجة.
المواضع الأربعة الشائعة
| الموضع | المثال الخاطئ | الصحيح |
|---|---|---|
| آخر عنصر | items[len(items)] | items[-1] |
| حلقة يدوية | range(len(items) + 1) | range(len(items)) |
| قائمة فارغة | first = items[0] | افحص if items: أولًا |
| نتيجة split | parts[1] بلا تحقق | افحص len(parts) |
الفهارس السالبة — هدية بايثون
tracks[-1] # آخر عنصر — بلا حساب طول ولا مخاطرة
tracks[-2] # قبل الأخير
💡 والشرائح slices لا ترمي الخطأ أبدًا:
tracks[5:10]على قائمة قصيرة ترجع[]بهدوء — مفيدة للاقتطاع الآمن.
الحلقة الصحيحة أصلًا لا تحتاج فهارس
# ❌ نمط قادم من لغات أخرى — عرضة للزلل
for i in range(len(tracks)):
print(tracks[i])
# ✅ بايثوني وآمن
for track in tracks:
print(track)
# ✅ وإن احتجت الفهرس فعلًا
for i, track in enumerate(tracks):
print(i, track)
ثانيًا: KeyError — مفاتيح القاموس
user = {"name": "أحمد", "age": 17}
print(user["email"]) # 💥 KeyError: 'email'
رسالة الخطأ تطبع اسم المفتاح الغائب بين علامتي اقتباس — اقرأها فهي نصف الحل (كثيرًا ما يكون السبب مجرد خطأ إملائي أو حالة أحرف).
طرق الوصول الآمن الأربع
# 1) get مع افتراضي — الأنسب في 90% من الحالات
email = user.get("email", "لا يوجد بريد")
# 2) فحص العضوية عندما يتفرع المنطق
if "email" in user:
send_mail(user["email"])
# 3) try/except عندما يكون الغياب استثناءً فعلًا
try:
email = user["email"]
except KeyError:
email = ask_user_for_email()
# 4) setdefault — اقرأ وأنشئ إن لم يوجد، بسطر واحد
tags = post.setdefault("tags", [])
tags.append("python")
defaultdict لقواميس التجميع
عند بناء «قاموس قوائم» يظهر KeyError في كل أول إضافة — defaultdict يحلها
من الجذر:
from collections import defaultdict
lessons_by_track = defaultdict(list)
for lesson in lessons:
# القائمة الفارغة تُنشأ تلقائيًا عند أول استخدام لمفتاح جديد
lessons_by_track[lesson.track].append(lesson)
وللعدّ خصيصًا، Counter أنظف وأخصّ:
from collections import Counter
level_counts = Counter(l.level for l in lessons)
قائمة تحقّق سريعة
- آخر فهرس صالح =
len - 1— وأفضل منه[-1]. - قبل
items[0]اسأل: هل يمكن أن تكون القائمة فارغة؟ range(len(items))بلا+ 1، والأفضل حلقة مباشرة أوenumerate.- اقرأ اسم المفتاح في رسالة KeyError — غالبًا typo أو حالة أحرف.
- الغياب طبيعي؟ →
get/in. تبني تجميعًا؟ →defaultdict.
الخلاصة
IndexError وKeyError ليسا عدوّين بل حارسا حدود: يوقفانك لحظة طلب
ما هو غير موجود بدل إعطائك قيمة خاطئة بصمت. احفظ قاعدة «آخر فهرس = الطول
ناقص واحد»، وامنح القواميس get وافتراضياتها، وستتحول الرسالتان الحمراوان
إلى ذكرى من أيام البداية.
رسّخ الأساس في مسار بايثون بالعربي، وأكمل سلسلة الأخطاء مع أخطاء بايثون الشائعة و حل SyntaxError: invalid syntax.