رفع خطاهای پایتون

خطای KeyError در پایتون؛ وقتی کلید در دیکشنری وجود ندارد

خطای KeyError در پایتون؛ وقتی کلید در دیکشنری وجود ندارد

🗓 آخرین به‌روزرسانی:

دیکشنری‌ها یکی از مهم‌ترین ساختارهای داده پایتون هستند؛ اما اگر کلیدی را بخواهید که داخل دیکشنری نیست، برنامه با KeyError متوقف می‌شود. این خطا در کار با داده‌های JSON، پاسخ API‌ها و فایل‌های تنظیمات بسیار پیش می‌آید.

معنی خطای KeyError در پایتون

KeyError یعنی با عملگر براکت d[key] سراغ کلیدی رفته‌اید که در دیکشنری d تعریف نشده است. پایتون در پیام خطا همان کلید را نشان می‌دهد. توجه کنید که کلیدها به نوع و حروف حساس‌اند: "Name" با "name" و عدد 1 با رشته "1" متفاوت است.

علت‌های رایج این خطا

  • غلط تایپی یا تفاوت حروف بزرگ و کوچک در نام کلید.
  • تفاوت نوع کلید، مثل 1 و "1".
  • داده‌ای که از API یا JSON می‌آید همیشه همه کلیدها را ندارد.
  • حذف کلید با del یا pop قبل از استفاده.
  • شمارش یا گروه‌بندی بدون مقداردهی اولیه کلید.

کد غلط و کد درست (با خروجی واقعی)

همه نمونه‌کدهای زیر با پایتون 3.13.5 اجرا شده‌اند و خروجی‌ها همان چیزی است که پایتون واقعاً نمایش می‌دهد.

مثال 1: خواندن کلید ناموجود

❌ کد غلط:

user = {"name": "Sara", "age": 25}
print(user["email"])

خروجی:

Traceback (most recent call last):
  File "/home/user/project/main.py", line 2, in <module>
    print(user["email"])
          ~~~~^^^^^^^^^
KeyError: 'email'

✅ کد درست:

user = {"name": "Sara", "age": 25}
print(user.get("email", "No email on file"))

خروجی:

No email on file

مثال 2: بررسی وجود کلید با in

❌ کد غلط:

scores = {"ali": 18}
scores["sara"] += 1

خروجی:

Traceback (most recent call last):
  File "/home/user/project/main.py", line 2, in <module>
    scores["sara"] += 1
    ~~~~~~^^^^^^^^
KeyError: 'sara'

✅ کد درست:

scores = {"ali": 18}
if "sara" not in scores:
    scores["sara"] = 0
scores["sara"] += 1
print(scores)

خروجی:

{'ali': 18, 'sara': 1}

مثال 3: تفاوت نوع کلید

❌ کد غلط:

prices = {1: 1000, 2: 2500}
print(prices["1"])

خروجی:

Traceback (most recent call last):
  File "/home/user/project/main.py", line 2, in <module>
    print(prices["1"])
          ~~~~~~^^^^^
KeyError: '1'

✅ کد درست:

prices = {1: 1000, 2: 2500}
print(prices[int("1")])

خروجی:

1000

راه‌حل قدم‌به‌قدم

  1. کلیدی که در پیام خطا آمده را با print(d.keys()) مقایسه کنید.
  2. املا، حروف بزرگ و کوچک و نوع کلید (عدد یا رشته) را بررسی کنید.
  3. اگر نبودن کلید طبیعی است، از d.get(key, default) استفاده کنید.
  4. برای شمارش و گروه‌بندی از setdefault یا collections.defaultdict بهره ببرید.
  5. در داده‌های خارجی (JSON و API) همیشه فرض کنید ممکن است کلیدی وجود نداشته باشد.

خواندن پیام KeyError و عیب‌یابی

KeyError وقتی رخ می‌دهد که با کروشه کلیدی را از دیکشنری بخواهید که وجود ندارد. پیام خطا خود کلید را نشان می‌دهد؛ دقت کنید که آیا نوع آن هم درست است، چون کلید 1 (عدد) با '1' (رشته) فرق دارد و این یکی از رایج‌ترین علت‌های پنهان KeyError است، مخصوصاً بعد از خواندن JSON.

برای تشخیص، کلیدهای موجود را با list(d.keys()) چاپ کنید و با کلید خواسته‌شده مقایسه کنید. فاصله اضافه، حروف بزرگ و کوچک و نوع داده را بررسی کنید. کد زیر سه روش دسترسی امن و تفاوت کلید عددی و رشته‌ای را نشان می‌دهد.

کلید رشته‌ای و عددی و دسترسی امن

import json

data = json.loads('{"1": "one", "name": "Ali"}')
print("Keys:", list(data.keys()))

try:
    data[1]
except KeyError as error:
    print(type(error).__name__, "-", error)

print("str key:", data["1"])
print("get:", data.get("age", 0))
print("in:", "name" in data)

خروجی (اجراشده با Python 3.13):

Keys: ['1', 'name']
KeyError - 1
str key: one
get: 0
in: True

قدم‌به‌قدم رفع KeyError

پیام خطا فقط خود کلید است؛ آن را با کلیدهای واقعی مقایسه کنید.

  1. کلید را با repr ببینید: print(repr(key)) فاصله اضافه یا نوع عدد و رشته را آشکار می‌کند.
  2. کلیدهای موجود را فهرست کنید: list(d.keys()) را چاپ کنید و حروف بزرگ و کوچک را مقایسه کنید.
  3. منبع داده را بررسی کنید: اگر داده از JSON یا API آمده، پاسخ واقعی را چاپ کنید؛ شاید ساختار تودرتو یا متفاوت باشد.
  4. مسیر تودرتو را قدم‌به‌قدم بروید: برای data[‘a’][‘b’]، اول data[‘a’] را جدا چاپ کنید تا معلوم شود کدام سطح کلید ندارد.
  5. روش دسترسی را انتخاب کنید: برای کلید اختیاری get و برای کلید ضروری پیام خطای روشن با try/except.

در پروژه‌های واقعی KeyError بیشتر هنگام کار با پاسخ API ها رخ می‌دهد، چون سرویس‌ها گاهی فیلدی را در بعضی پاسخ‌ها حذف می‌کنند یا نام آن را در نسخه جدید تغییر می‌دهند. پاسخ را یک بار کامل با json.dumps(data, indent=2) چاپ کنید تا ساختار واقعی را ببینید. برای فیلدهای ضروری، پیام خطای روشن بدهید که بگوید کدام کلید و در کدام پاسخ وجود نداشته است؛ این کار عیب‌یابی بعدی را بسیار سریع‌تر می‌کند. برای ساختارهای پیچیده هم می‌توانید از کتابخانه‌هایی مثل pydantic استفاده کنید. همچنین هنگام ادغام دو دیکشنری با | یا update، کلیدهای مشترک بازنویسی می‌شوند؛ بعد از ادغام، وجود کلیدهای ضروری را دوباره بررسی کنید تا خطا در مراحل بعدی ظاهر نشود.

چک‌لیست پیشگیری

  • برای کلیدهای اختیاری همیشه از get با مقدار پیش‌فرض استفاده کنید.
  • بعد از json.loads به یاد داشته باشید که همه کلیدها رشته هستند.
  • کلیدهای ورودی کاربر را با strip و lower نرمال کنید.
  • برای شمارش از collections.Counter و برای گروه‌بندی از defaultdict استفاده کنید.
  • در pandas قبل از df['col'] نام ستون‌ها را با df.columns بررسی کنید.

📌 همچنین بخوانید: خطای unexpected EOF while parsing در پایتون، خطای ‘int’ object is not subscriptable در پایتون و خطای ImportError

سؤالات متداول

فرق d[key] و d.get(key) چیست؟

d[key] در نبود کلید خطای KeyError می‌دهد، اما d.get(key) مقدار None یا مقدار پیش‌فرضی که تعیین کرده‌اید را برمی‌گرداند.

KeyError در pandas هم همین است؟

بله؛ در pandas وقتی نام ستونی را بخواهید که وجود ندارد همین خطا را می‌گیرید. نام ستون‌ها را با df.columns بررسی کنید.

آیا گرفتن KeyError با try/except کار درستی است؟

بله، به‌خصوص وقتی نبود کلید یک حالت استثنایی است. اما اگر نبود کلید رایج است، get() یا in خواناتر است.

چرا بعد از خواندن JSON کلید عددی پیدا نمی‌شود؟

JSON فقط کلید رشته‌ای دارد؛ بنابراین کلید 1 بعد از ذخیره و خواندن به '1' تبدیل می‌شود.

pop هم KeyError می‌دهد؟

بله، d.pop(key) در نبود کلید خطا می‌دهد؛ d.pop(key, None) نسخه امن آن است.

KeyError در os.environ یعنی چه؟

یعنی متغیر محیطی تعریف نشده است؛ از os.environ.get('NAME') یا os.getenv استفاده کنید.

آموزش‌های مرتبط

آموزش‌های تازه، کد آماده و رفع خطاهر هفته در کانال Dev Source؛ رایگان.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *