أثناء التعامل مع القواميس Dictionary في Python، قد تكتب كودًا بسيطًا لقراءة قيمة من قاموس، ثم يظهر لك خطأ باسم KeyError. هذا الخطأ شائع جدًا عند المبتدئين، وغالبًا سببه أنك طلبت مفتاحًا غير موجود داخل القاموس.
المشكلة لا تظهر فقط في الأمثلة الصغيرة، بل تظهر كثيرًا في البرامج العملية عند قراءة بيانات من JSON أو CSV أو API، لأن البيانات الخارجية لا تكون دائمًا بنفس الشكل الذي تتوقعه. قد يكون المفتاح ناقصًا، أو مكتوبًا بحرف كبير بدل صغير، أو يحتوي على مسافة زائدة، أو أن بعض السجلات تحتوي عليه وبعضها لا يحتوي عليه.
في هذا المقال الموسع من سلسلة مشكلة وحل على موقع بايثون العرب سنشرح معنى خطأ KeyError، ولماذا يظهر عند التعامل مع Dictionary، وكيف تحله بطرق عملية مثل استخدام get()، وفحص وجود المفتاح باستخدام in، واستخدام setdefault()، والتعامل مع الخطأ باستخدام try و except، واستخدام defaultdict في حالات العدّ والتجميع.
{getToc} $title={محتوى المقال}
الفكرة ببساطة: خطأKeyErrorيظهر عندما تطلب من Dictionary قيمة مفتاح غير موجود. الحل أن تفحص المفتاح قبل استخدامه أو تستخدمget()بدل الوصول المباشر عندما يكون المفتاح اختياريًا. {alertInfo}
شكل خطأ KeyError في Python
قد يظهر الخطأ بهذا الشكل المختصر:
KeyError: 'grade'
أو يظهر داخل رسالة Traceback كاملة:
Traceback (most recent call last):
File "main.py", line 6, in <module>
print(student["grade"])
KeyError: 'grade'
معنى الرسالة أن Python حاول الوصول إلى المفتاح "grade" داخل القاموس، لكنه لم يجده.
إذا كنت لا تعرف كيف تقرأ رسالة الخطأ من البداية، راجع درس: أساسيات بايثون 32: شرح أخطاء Python وقراءة رسائل الخطأ لأن فهم آخر سطر في الخطأ يساعدك كثيرًا في معرفة السبب بسرعة.
مثال بسيط يسبب KeyError
لنفترض أن لدينا قاموسًا يحتوي على بيانات طالب:
student = {
"name": "Ali",
"age": 20
}
print(student["grade"])
الكود يحاول قراءة المفتاح "grade"، لكن القاموس يحتوي فقط على "name" و "age". لذلك يظهر:
KeyError: 'grade'
ما معنى KeyError؟
كلمة Key تعني "مفتاح"، و Error تعني "خطأ". إذن KeyError يعني أن هناك مشكلة في المفتاح الذي تستخدمه داخل القاموس.
في Python، القاموس يتكون من أزواج:
student = {
"name": "Ali",
"age": 20
}
كل عنصر يتكون من:
- مفتاح: مثل
"name". - قيمة: مثل
"Ali".
لذلك عندما تكتب:
student["name"]
فأنت تطلب من Python: أعطني القيمة المرتبطة بالمفتاح "name". إذا كان المفتاح موجودًا، تحصل على القيمة. وإذا لم يكن موجودًا، يظهر KeyError.
إذا كنت تحتاج مراجعة أساسيات القواميس أولًا، يمكنك الرجوع إلى: شرح Dictionary في Python للمبتدئين.
السبب الأول: المفتاح غير موجود أصلًا
هذا هو السبب الأكثر شيوعًا. القاموس لا يحتوي على المفتاح الذي تحاول قراءته.
مثال:
user = {
"username": "aboarab",
"email": "user@example.com"
}
print(user["phone"])
المفتاح "phone" غير موجود، لذلك سيظهر KeyError.
السبب الثاني: اختلاف اسم المفتاح
أحيانًا يكون المفتاح موجودًا، لكنك تكتبه بطريقة مختلفة.
مثال:
student = {
"Name": "Sara"
}
print(student["name"])
هنا المفتاح الموجود هو "Name" بحرف كبير، لكننا طلبنا "name" بحرف صغير. في Python، النصوص حساسة لحالة الأحرف، لذلك "Name" لا تساوي "name".
انتبه: مفاتيح Dictionary حساسة لحالة الأحرف. المفتاح"Name"يختلف عن"name". {alertWarning}
السبب الثالث: وجود مسافات في اسم المفتاح
قد يأتي المفتاح من ملف CSV أو JSON وفيه مسافة غير ظاهرة بوضوح.
مثال:
row = {
"name ": "Ali",
"grade": "90"
}
print(row["name"])
المفتاح الموجود فعليًا هو "name " وفيه مسافة في النهاية، بينما أنت طلبت "name" بدون مسافة.
لفحص المفاتيح الموجودة، اطبع:
print(row.keys())
السبب الرابع: التعامل مع بيانات خارجية غير ثابتة
عندما تقرأ بيانات من API أو JSON أو CSV، قد لا تكون كل الصفوف أو كل السجلات تحتوي على نفس المفاتيح.
مثال:
users = [
{"name": "Ali", "email": "ali@example.com"},
{"name": "Sara"},
{"name": "Omar", "email": "omar@example.com"}
]
for user in users:
print(user["email"])
العنصر الثاني لا يحتوي على "email"، لذلك سيظهر KeyError.
السبب الخامس: الخلط بين قيمة المفتاح واسم المفتاح
أحيانًا يخلط المبتدئ بين اسم المفتاح والقيمة الموجودة داخله.
student = {
"name": "Ali",
"age": 20
}
print(student["Ali"])
القيمة "Ali" موجودة، لكنها ليست مفتاحًا. المفتاح الصحيح هو "name".
الصحيح:
print(student["name"])
الحل السريع لمن يريد حل المشكلة مباشرة
إذا ظهر لك KeyError، نفذ هذه الخطوات:
- اقرأ اسم المفتاح الموجود في آخر سطر من الخطأ.
- اطبع المفاتيح الموجودة باستخدام
keys(). - تأكد من الاسم والحروف الكبيرة والصغيرة والمسافات.
- إذا كان المفتاح اختياريًا استخدم
get(). - إذا كان المفتاح ضروريًا، راجع مصدر البيانات لأن البيانات ناقصة.
مثال سريع:
print(student.keys())
grade = student.get("grade", "لا توجد درجة")
print(grade)
الحل الأول: استخدام get لتجنب KeyError
أفضل حل للمبتدئين غالبًا هو استخدام get(). هذه الدالة تحاول قراءة المفتاح، وإذا لم يكن موجودًا ترجع قيمة افتراضية بدل أن توقف البرنامج.
بدل هذا:
print(student["grade"])
اكتب:
print(student.get("grade"))
إذا لم يكن المفتاح موجودًا، سترجع None بدل ظهور KeyError.
ويمكنك تحديد قيمة افتراضية:
grade = student.get("grade", "غير محدد")
print(grade)
مثال عملي باستخدام get
student = {
"name": "Ali",
"age": 20
}
name = student.get("name", "بدون اسم")
grade = student.get("grade", "لا توجد درجة")
print(name)
print(grade)
الناتج:
Ali
لا توجد درجة
بهذه الطريقة يستمر البرنامج بدل أن يتوقف بسبب خطأ.
متى يكون get أفضل من الوصول المباشر؟
استخدم get() عندما يكون غياب المفتاح طبيعيًا أو متوقعًا. مثل:
- رقم الهاتف اختياري في بيانات المستخدم.
- البريد الإلكتروني قد لا يوجد في كل سجل.
- بعض صفوف CSV ناقصة البيانات.
- بعض بيانات JSON القادمة من API لا تحتوي على نفس المفاتيح دائمًا.
مثال:
phone = user.get("phone", "لا يوجد رقم هاتف")
الحل الثاني: فحص وجود المفتاح باستخدام in
يمكنك فحص وجود المفتاح قبل استخدامه:
if "grade" in student:
print(student["grade"])
else:
print("لا توجد درجة")
هذه الطريقة مناسبة عندما تريد تنفيذ منطق مختلف إذا كان المفتاح موجودًا أو غير موجود.
مثال عملي على in
settings = {
"theme": "dark",
"language": "ar"
}
if "notifications" in settings:
print(settings["notifications"])
else:
print("إعداد التنبيهات غير موجود")
الناتج:
إعداد التنبيهات غير موجود
متى أستخدم in بدل get؟
استخدم in عندما لا تريد فقط قيمة بديلة، بل تريد اتخاذ قرار مختلف حسب وجود المفتاح.
مثال:
if "discount" in product:
final_price = product["price"] - product["discount"]
else:
final_price = product["price"]
الحل الثالث: استخدام try و except KeyError
إذا كان الوصول المباشر للمفتاح مناسبًا في منطق برنامجك، لكنك تريد التعامل مع الخطأ عند حدوثه، يمكنك استخدام try و except.
student = {
"name": "Sara",
"age": 22
}
try:
print(student["grade"])
except KeyError:
print("المفتاح grade غير موجود داخل بيانات الطالب")
هذه الطريقة مفيدة عندما يكون الخطأ متوقعًا، لكن لا تجعلها طريقة لإخفاء مشاكل البيانات دائمًا.
لشرح أوسع عن try و except راجع:
أساسيات بايثون 31: شرح try و except في Python.
الحل الرابع: استخدام setdefault
الدالة setdefault() تفحص المفتاح. إذا كان موجودًا، ترجع قيمته. وإذا لم يكن موجودًا، تضيفه إلى القاموس بقيمة افتراضية ثم ترجعها.
مثال:
student = {
"name": "Ali"
}
grade = student.setdefault("grade", 0)
print(grade)
print(student)
الناتج:
0
{'name': 'Ali', 'grade': 0}
استخدم setdefault() عندما تريد إضافة المفتاح تلقائيًا إذا لم يكن موجودًا. أما إذا كنت لا تريد تعديل القاموس، فاستخدم get().
الحل الخامس: استخدام defaultdict في حالات العدّ والتجميع
في بعض البرامج، خاصة عند العدّ أو التجميع، يظهر KeyError لأنك تحاول زيادة قيمة مفتاح قبل إنشائه.
مثال خاطئ:
words = ["python", "code", "python"]
counts = {}
for word in words:
counts[word] += 1
في أول مرة تظهر كلمة "python"، المفتاح غير موجود، لذلك يظهر KeyError.
حل بسيط باستخدام get():
words = ["python", "code", "python"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)
الناتج:
{'python': 2, 'code': 1}
وحل أكثر تقدمًا باستخدام defaultdict:
from collections import defaultdict
words = ["python", "code", "python"]
counts = defaultdict(int)
for word in words:
counts[word] += 1
print(dict(counts))
الناتج:
{'python': 2, 'code': 1}
الفرق بين الوصول المباشر و get و in و setdefault
| الطريقة | ماذا تفعل؟ | متى أستخدمها؟ |
|---|---|---|
dict["key"] |
تقرأ المفتاح مباشرة وقد تسبب KeyError |
عندما تكون متأكدًا أن المفتاح موجود وأن غيابه خطأ حقيقي |
dict.get("key") |
ترجع القيمة أو None إذا لم يوجد المفتاح |
عندما يكون غياب المفتاح طبيعيًا |
dict.get("key", default) |
ترجع قيمة افتراضية عند غياب المفتاح | عندما تريد نتيجة بديلة واضحة |
"key" in dict |
تفحص هل المفتاح موجود | عندما تريد تنفيذ شرط حسب وجود المفتاح |
dict.setdefault("key", default) |
يرجع المفتاح أو يضيفه بقيمة افتراضية | عندما تريد إنشاء المفتاح إذا لم يكن موجودًا |
defaultdict |
ينشئ قيمة افتراضية تلقائيًا للمفاتيح الجديدة | في العدّ والتجميع وبعض الهياكل المتكررة |



