أثناء التعامل مع القواميس Dictionary في Python، قد تكتب كودًا بسيطًا لقراءة قيمة من قاموس، ثم يظهر لك خطأ باسم KeyError. هذا الخطأ شائع جدًا عند المبتدئين، وغالبًا سببه أنك طلبت مفتاحًا غير موجود داخل القاموس.
المشكلة لا تظهر فقط في الأمثلة الصغيرة، بل تظهر كثيرًا في البرامج العملية عند قراءة بيانات من JSON أو CSV أو API، لأن البيانات الخارجية لا تكون دائمًا بنفس الشكل الذي تتوقعه. قد يكون المفتاح ناقصًا، أو مكتوبًا بحرف كبير بدل صغير، أو يحتوي على مسافة زائدة، أو أن بعض السجلات تحتوي عليه وبعضها لا يحتوي عليه.
في هذا المقال الموسع من سلسلة مشكلة وحل على موقع بايثون العرب سنشرح معنى خطأ KeyError، ولماذا يظهر عند التعامل مع Dictionary، وكيف تحله بطرق عملية مثل استخدام get()، وفحص وجود المفتاح باستخدام in، واستخدام setdefault()، والتعامل مع الخطأ باستخدام try و except، واستخدام defaultdict في حالات العدّ والتجميع.
{getToc} $title={محتوى المقال}
الفكرة ببساطة: خطأKeyErrorيظهر عندما تطلب من Dictionary قيمة مفتاح غير موجود. الحل أن تفحص المفتاح قبل استخدامه أو تستخدمget()بدل الوصول المباشر عندما يكون المفتاح اختياريًا. {alertInfo}
حل KeyError بسرعة: اختر الطريقة حسب حالة المفتاح
إذا ظهر لك KeyError، لا تستبدل الأقواس بـ get() تلقائيًا في كل مرة. اختر الحل بحسب معنى غياب المفتاح في برنامجك:
| الحالة | الطريقة المناسبة | لماذا؟ |
|---|---|---|
| المفتاح يجب أن يكون موجودًا | dict["key"] |
دع الخطأ يظهر حتى تكتشف مشكلة البيانات أو منطق البرنامج. |
| المفتاح اختياري وتريد قيمة بديلة | dict.get("key", default) |
تعيد قيمة افتراضية بدل KeyError. |
| تحتاج تنفيذ منطق مختلف حسب وجود المفتاح | "key" in dict |
تفحص الوجود قبل القراءة. |
| الوصول المباشر مناسب لكنك تريد معالجة الخطأ | try/except KeyError |
تتعامل مع الاستثناء عند حدوثه. |
تشخيص سريع: اقرأ اسم المفتاح في آخر سطر من الخطأ، ثم اطبع data.keys() وتحقق من الكتابة وحالة الأحرف والمسافات وشكل البيانات. إذا كان المفتاح إلزاميًا، أصلح مصدر البيانات بدل إخفاء المشكلة بقيمة افتراضية.
شكل خطأ KeyError في Python
قد يظهر الخطأ بهذا الشكل المختصر:
KeyError: 'grade'
أو يظهر داخل رسالة Traceback كاملة:
Traceback (most recent call last):
File "main.py", line 6, in <module>
print(student["grade"])
KeyError: 'grade'
معنى الرسالة أن Python حاول الوصول إلى المفتاح "grade" داخل القاموس، لكنه لم يجده.
ماذا تعني رسائل مثل KeyError: 0 أو KeyError: 'name'؟
القيمة التي تظهر بعد KeyError: هي المفتاح الذي حاول Python العثور عليه ولم يجده. فإذا ظهر KeyError: 'name' فهذا يعني أن المفتاح النصي "name" غير موجود، وإذا ظهر KeyError: 0 فهذا يعني أن الكود حاول استخدام العدد 0 كمفتاح داخل القاموس ولم يجده.
لا يعني KeyError: 0 أن المشكلة مرتبطة بالفهرس بالضرورة؛ القواميس تستخدم مفاتيح، وقد يكون المفتاح عددًا أو نصًا أو نوعًا آخر قابلًا للاستخدام كمفتاح.
إذا كنت لا تعرف كيف تقرأ رسالة الخطأ من البداية، راجع درس: أساسيات بايثون 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 في القواميس المتداخلة Nested Dictionary
قد يكون المفتاح الأول موجودًا لكن مفتاحًا أعمق مفقودًا. مثلًا، في التعبير data["user"]["profile"]["name"] سيظهر KeyError إذا كان أي مفتاح في المسار غير موجود.
data = {
"user": {
"name": "Ali"
}
}
print(data["user"]["profile"]["name"])
إذا كانت البنية اختيارية، يمكنك استخدام قيمة افتراضية فارغة في المستويات الوسطى:
name = (
data.get("user", {})
.get("profile", {})
.get("name", "غير معروف")
)
print(name)
أما إذا كانت "profile" أو "name" مفاتيح إلزامية في بنية بياناتك، فلا تستخدم get() فقط لإخفاء المشكلة؛ افحص مصدر JSON أو API وأصلح البيانات أو التحقق منها.
خطوات تشخيص KeyError قبل اختيار الحل
بعد معرفة معنى الخطأ، استخدم هذه الخطوات لتحديد سببه الحقيقي:
- اقرأ اسم المفتاح الموجود في آخر سطر من الخطأ.
- اطبع المفاتيح الموجودة باستخدام
keys(). - تأكد من الاسم والحروف الكبيرة والصغيرة والمسافات.
- إذا كان المفتاح اختياريًا استخدم
get(). - إذا كان المفتاح ضروريًا، راجع مصدر البيانات لأن البيانات ناقصة.
مثال سريع:
print(student.keys())
grade = student.get("grade", "لا توجد درجة")
print(grade)
الحل الأول: استخدام get لتجنب KeyError
استخدم get() عندما يكون غياب المفتاح طبيعيًا أو متوقعًا. هذه الدالة تحاول قراءة المفتاح، وإذا لم يكن موجودًا ترجع None أو قيمة افتراضية بدل أن توقف البرنامج. أما إذا كان المفتاح إلزاميًا، فقد يكون ظهور KeyError مفيدًا لأنه يكشف مشكلة حقيقية في البيانات أو منطق البرنامج.
بدل هذا:
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}
مقارنة سريعة بين طرق التعامل مع المفتاح المفقود
| الطريقة | عند غياب المفتاح | الاستخدام الأنسب |
|---|---|---|
dict["key"] |
ترفع KeyError |
عندما يجب أن يكون المفتاح موجودًا وغيابه مشكلة حقيقية. |
dict.get("key", default) |
تعيد القيمة الافتراضية | للمفاتيح الاختيارية أو البيانات الخارجية غير المكتملة. |
"key" in dict |
تعطي False |
عندما تريد تنفيذ منطق مختلف حسب وجود المفتاح. |
try/except KeyError |
يلتقط الاستثناء | عندما يكون الوصول المباشر مناسبًا لكنك تريد معالجة حالة الغياب. |
setdefault() |
يضيف المفتاح بقيمة افتراضية | عندما تريد إنشاء المفتاح إذا لم يكن موجودًا. |
defaultdict |
ينشئ قيمة افتراضية تلقائيًا | للعدّ والتجميع والهياكل التي تنشئ مفاتيح أثناء العمل. |
أسئلة شائعة
ما سبب KeyError في Python؟
يظهر KeyError غالبًا عندما تستخدم dict[key] بمفتاح غير موجود داخل القاموس. وقد يكون السبب خطأ في اسم المفتاح أو اختلاف حالة الأحرف أو مسافة زائدة أو بيانات JSON/API لا تحتوي على المفتاح المتوقع.
ما الفرق بين dict["key"] و dict.get("key")؟
الوصول باستخدام الأقواس يرفع KeyError إذا كان المفتاح غير موجود. أما get() فتعيد None أو قيمة افتراضية تحددها. استخدم الأقواس عندما يكون المفتاح إلزاميًا، وget() عندما يكون غيابه طبيعيًا.
كيف أحل KeyError: 0؟
هذا يعني أن الكود حاول الوصول إلى المفتاح العددي 0 داخل القاموس ولم يجده. اطبع dict.keys() وتأكد من أن المفتاح الموجود هو فعلًا العدد 0 وليس النص "0" أو مفتاحًا آخر.
كيف أتجنب KeyError في JSON أو API؟
بعد تحويل JSON إلى بيانات Python، افحص بنية البيانات أولًا. استخدم get() للمفاتيح الاختيارية، وراجع مصدر البيانات إذا كان المفتاح مطلوبًا لكنه مفقود. في القواميس المتداخلة افحص كل مستوى من المسار.
الخلاصة
خطأ KeyError لا يعني أن القاموس نفسه تالف؛ بل يعني أن Python لم يجد المفتاح الذي طلبته. ابدأ بقراءة اسم المفتاح في رسالة الخطأ وطباعة keys()، ثم اختر الحل المناسب حسب ما إذا كان المفتاح إلزاميًا أم اختياريًا.
- استخدم
dict["key"]عندما يجب أن يكون المفتاح موجودًا. - استخدم
get()عندما يكون المفتاح اختياريًا أو تريد قيمة بديلة. - استخدم
inعندما تحتاج إلى منطق مختلف حسب وجود المفتاح. - استخدم
try/except KeyErrorعندما تريد معالجة الاستثناء صراحة. - راجع البنية بعناية مع JSON وAPI والقواميس المتداخلة.
القاعدة العملية: لا تُخفِKeyErrorتلقائيًا. إذا كان غياب المفتاح خطأ في بياناتك، أصلح السبب؛ وإذا كان الغياب طبيعيًا، استخدم طريقة وصول آمنة مثلget(). {alertSuccess}



