مدیریت خطای KeyError در پایتون
خطای KeyError پایتون یک استثنا رایج است که مبتدیان با آن مواجه می شوند. دانستن اینکه چرا یک KeyError میتواند ایجاد شود و برخی راهحلها برای جلوگیری از توقف برنامه شما، گامهای اساسی برای بهبود به عنوان یک برنامهنویس پایتون هستند.
# خطای KeyError پایتون به چه معنی است؟
یک استثنای KeyError پایتون چیزی است که هنگام تلاش برای دسترسی به کلیدی که در دیکشنری نیست ایجاد می شود.
مستندات رسمی پایتون می گوید که KeyError زمانی که به یک کلید mapping دسترسی پیدا می شود افزایش می یابد و در mapping یافت نمی شود. mapping یک ساختار داده است که یک مجموعه از مقادیر را به مجموعه ای دیگر ارسال می کند. رایج ترین mapping در پایتون دیکشنری است.
KeyError پایتون یک نوع استثنای LookupError است و نشان می دهد که مشکلی در بازیابی کلید مورد نظر شما وجود داشته است. وقتی یک KeyError می بینید، معنای آن این است که کلید مورد جستجو پیدا نشد.
در مثال زیر یک دیکشنری (ages) تعریف شده با سن سه نفر را که مشاهده می کنید. هنگامی که سعی می کنید به کلیدی دسترسی پیدا کنید که در دیکشنری وجود ندارد، یک KeyError ظاهر می شود:
>>> ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
>>> ages['Michael']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Michael'
در اینجا، تلاش برای دسترسی به کلید «مایکل» در دیکشنری ages، منجر به ایجاد یک خطای KeyError میشود. در انتهای traceback، اطلاعات مربوطه را دریافت می کنید:
- این واقعیت که یک KeyError مطرح شد.
- کلیدی که پیدا نشد، «Michael» بود.
خط دوم به آخر به شما می گوید که کدام خط استثنا را مطرح کرده است. این اطلاعات زمانی که کد پایتون را از یک فایل اجرا می کنید مفیدتر است.
در برنامه زیر می توانید دوباره دیکشنری ages را که تعریف شده ببینید. این بار از شما خواسته می شود نام فردی را برای بازیابی سن مشخص کنید:
# ages.py
ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
person = input('Get age for: ')
print(f'{person} is {ages[person]} years old.')
این کد نامی را که شما در همان لحظه ارائه میکنید میگیرد و سعی میکند سن آن شخص را بازیابی کند. هر آنچه را که در prompt تایپ کنید به عنوان کلید دیکشنری ages در خط 4 استفاده می شود.
با تکرار مثال ناموفق بالا، یک traceback دیگر دریافت می کنیم، این بار با اطلاعاتی در مورد خط موجود در فایل که KeyError را ایجاد کرده:
$ python ages.py
Get age for: Michael
Traceback (most recent call last):
File "ages.py", line 4, in <module>
print(f'{person} is {ages[person]} years old.')
KeyError: 'Michael'
وقتی کلیدی را بدهید که در دیکشنری نیست، برنامه از کار می افتد. در اینجا، چند خط آخر traceback به مشکل اشاره می کند. فایل "ages.py"، خط 4، در <module> به شما می گوید که کدام خط از کدام فایل، استثنای KeyError را ایجاد کرده است. سپس آن خط به شما نشان داده می شود. در نهایت، استثنای KeyError کلید گم شده را فراهم می کند.
بنابراین میتوانید ببینید که خط پایانی KeyError به تنهایی اطلاعات کافی را در اختیار شما قرار نمیدهد، اما خطوط قبل از آن میتواند شما را به درک اشتباهی که رخ داده است نزدیکتر کند.
ویدیو پیشنهادی: ویدیو توضیح مدیریت خطاها در پایتون
# خطای KeyError در کتابخانه استاندارد پایتون
در اکثر مواقع، خطای KeyError پایتون به این دلیل مطرح میشود که کلیدی در دیکشنری یا زیرکلاس دیکشنری (مانند os.environ) یافت نمیشود.
در موارد نادر، اگر موردی در بایگانی ZIP یافت نشود، ممکن است آن را در مکانهای دیگر کتابخانه استاندارد پایتون، مانند ماژول فایل zip، مشاهده کنید. با این حال، این مکانها همان معنای KeyError پایتون را حفظ میکنند، که کلید درخواستی را پیدا نمیکند.
در مثال زیر، می توانید استفاده از کلاس zipfile.ZipFile را برای استخراج اطلاعات در مورد یک آرشیو ZIP با استفاده از .getinfo () ببینید:
>>> from zipfile import ZipFile
>>> zip_file = ZipFile('the_zip_file.zip')
>>> zip_file.getinfo('something')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/to/python/installation/zipfile.py", line 1304, in getinfo
'There is no item named %r in the archive' % name)
KeyError: "There is no item named 'something' in the archive"
این واقعاً شبیه جستجوی کلید دیکشنری نیست. در عوض، یک فراخوانی به zipfile.ZipFile.getinfo() است که استثنا را ایجاد می کند.
آخرین چیزی که در اینجا باید به آن توجه کنید این است که خطی که KeyError را ایجاد میکند در کد شما نیست. این در کد zipfile است، اما خطوط قبلی traceback نشان می دهد که کدام خطوط در کد شما باعث ایجاد مشکل شده است.
ویدیو پیشنهادی: ویدیو آموزش dictionary در پایتون
# خطای KeyError سفارشی در پایتون
ممکن است مواقعی برای شما منطقی باشد که یک استثنای KeyError پایتون در کد خود ایجاد کنید. این را می توان با استفاده از کلمه کلیدی raise و فراخوانی استثنای KeyError انجام داد:
raise KeyError(message)
معمولاً message کلید گم شده است. با این حال، مانند بسته zipfile، میتوانید اطلاعات بیشتری ارائه دهید تا به توسعهدهنده بعدی کمک کنید تا بهتر بفهمد چه مشکلی رخ داده است.
اگر تصمیم دارید یک خطای KeyError پایتون را در کد خود مطرح کنید، فقط مطمئن شوید که مورد استفاده شما با معنای پشت استثنا مطابقت دارد. این باید نشان دهد که کلید مورد جستجو یافت نشد.
مقاله پیشنهادی: نوع داده None در پایتون
# چطور خطای KeyError را در پایتون مدیریت کنیم؟
هنگامی که با KeyError مواجه می شوید، چند راه استاندارد برای رسیدگی به آن وجود دارد. بسته به مورد استفاده شما، برخی از این راه حل ها ممکن است بهتر از بقیه باشند. هدف نهایی جلوگیری از ایجاد استثناهای KeyError غیرمنتظره است.
+ روش معمول: ()get.
اگر KeyError از جستجوی ناموفق کلید دیکشنری در کد شما ایجاد شده باشد، می توانید از .get() برای برگرداندن مقدار یافت شده در کلید مشخص شده یا یک مقدار پیش فرض استفاده کنید.
دقیقاً مانند مثال بازیابی سن، مثال زیر روش بهتری برای دریافت سن از دیکشنری با استفاده از کلید ارائه شده در اعلان نشان می دهد:
# ages.py
ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
person = input('Get age for: ')
age = ages.get(person)
if age:
print(f'{person} is {age} years old.')
else:
print(f"{person}'s age is unknown.")
در اینجا، خط 5 نشان می دهد که چگونه می توانید مقدار age را با استفاده از .get() دریافت کنید. این باعث میشود که متغیر age دارای مقدار سن موجود در دیکشنری برای کلید ارائهشده یا یک مقدار پیشفرض، در این مورد None باشد.
این بار، به دلیل استفاده از روش ایمنتر .get() برای دریافت سن به جای تلاش برای دسترسی مستقیم به کلید، یک استثنای KeyError ایجاد نمیکنید:
$ python ages.py
Get age for: Michael
Michael's age is unknown.
در اجرای مثال بالا، KeyError دیگر زمانی که یک کلید اشتباه ارائه می شود، ایجاد نمیشود. کلید "Michael" در دیکشنری یافت نمی شود، اما با استفاده از .get()، به جای یک KeyError، یک None برگردانده می شود.
متغیر age یا سن فرد در دیکشنری را برمیگرداند یا مقدار پیش فرض (به طور پیش فرض None). همچنین می توانید با ارسال آرگومان دوم، مقدار پیش فرض متفاوتی را در فراخوانی .get() تعیین کنید.
این کد خط 5 مثال بالاست که از get به روش دیگری استفاده کرده و آرگومان دوم را نیز ارسال کرده:
age = ages.get(person, 0)
در اینجا، به جای اینکه «Michael» مقدار None را برگرداند، 0 را برمیگرداند زیرا کلید پیدا نشد و مقدار پیشفرض برای بازگشت اکنون 0 است.
مقاله پیشنهادی: معرفی کلمات کلیدی در پایتون
+ روش غیرمعمول: جستجوی کلید
مواقعی وجود دارد که باید وجود یک کلید را در دیکشنری مشخص کنید. در این موارد، استفاده از .get() ممکن است اطلاعات صحیحی را در اختیار شما قرار ندهد. دریافت None از فراخوانی .get() میتواند به این معنی باشد که کلید پیدا نشده است یا مقدار یافت شده در کلید در دیکشنری در واقع None است.
با دیکشنری یا شی دیکشنری مانند، میتوانید از عملگر in برای تعیین اینکه آیا یک کلید در mapping وجود دارد یا خیر استفاده کنید. این عملگر یک مقدار بولی (درست یا نادرست) برمی گرداند که نشان می دهد آیا کلید در دیکشنری یافت می شود یا خیر.
در این مثال، شما یک دیکشنری response را از فراخوانی یک API دریافت می کنید. این response ممکن است دارای یک مقدار کلید خطا باشد که در پاسخ تعریف شده است، که نشان می دهد response در حالت خطا است:
# parse_api_response.py
...
# Assuming you got a `response` from calling an API that might
# have an error key in the `response` if something went wrong
if 'error' in response:
... # Parse the error state
else:
... # Parse the success state
در اینجا، بررسی وجود کلید error در response و گرفتن مقدار پیش فرض از کلید تفاوت دارد. این یک مورد نادر است که در آن چیزی که در واقع به دنبال آن هستید این است که آیا کلید در دیکشنری وجود دارد و نه مقدار آن کلید.
مقاله پیشنهادی: درک traceback پایتون
+ روش کلی: try...except
مانند هر استثنایی، همیشه میتوانید از بلوک try...except برای جداسازی کد ایجاد کننده استثناء بالقوه و ارائه یک راهحل پشتیبان استفاده کنید.
میتوانید در مثالی مشابه قبل، از try except استفاده کنید، اما این بار یک پیام پیشفرض برای چاپ در صورتی که یک KeyError در حالت عادی ایجاد شود، ارائه دهید:
# ages.py
ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
person = input('Get age for: ')
try:
print(f'{person} is {ages[person]} years old.')
except KeyError:
print(f"{person}'s age is unknown.")
در اینجا، می توانید حالت عادی را در بلوک try برای چاپ نام و سن فرد مشاهده کنید. کد پشتیبان در بلوک except قرار دارد، جایی که اگر یک KeyError در حالت معمولی مطرح شود، کد پشتیبان باید پیام دیگری را چاپ کند.
راه حل try...except نیز یک راه حل عالی برای جاهایی است که ممکن است از .get() یا عملگر in پشتیبانی نکنند. همچنین اگر KeyError از کد شخص دیگری ایجاد شده باشد، بهترین راه حل است.
در اینجا یک مثال با استفاده از پکیج zipfile دوباره آورده شده است. این بار، بلوک try...except راهی به ما می دهد تا از توقف برنامه استثنای KeyError جلوگیری کنیم:
>>> from zipfile import ZipFile
>>> zip = ZipFile('the_zip_file.zip')
>>> try:
... zip.getinfo('something')
... except KeyError:
... print('Can not find "something"')
...
Can not find "something"
از آنجایی که کلاس ZipFile مانند دیکشنری متد .get را ارائه نمی دهد، باید از try...except استفاده کنید. در این مثال، لازم نیست از قبل بدانید چه مقادیری برای ارسال به .getinfo() معتبر هستند.
# نتیجه گیری
اکنون برخی از مکانهای رایجی را میشناسید که در آنها استثنای KeyError پایتون میتواند مطرح شود و راهحلهای عالی را میشناسید که میتوانید از آنها برای جلوگیری از توقف برنامه خود استفاده کنید.
اکنون، دفعه بعد که یک KeyError را مشاهده کردید، متوجه خواهید شد که احتمالاً فقط یک جستجوی کلید اشتباه دیکشنری است. همچنین میتوانید تمام اطلاعاتی را که برای تعیین اینکه خطا از کجا آمده است، با نگاه کردن به چند خط آخر traceback پیدا کنید.
اگر مشکل جستجوی کلید دیکشنری در کد شما باشد، میتوانید از دسترسی مستقیم به کلید در دیکشنری با استفاده از روش ایمنتر .get() با مقدار بازگشتی پیشفرض تغییر دهید. اگر مشکل از کد خود شما نیست، استفاده از بلوک try...except بهترین گزینه برای کنترل جریان کد شماست.
استثناها نباید ترسناک باشند. هنگامی که بدانید چگونه اطلاعات ارائه شده در traceback آنها و علت اصلی استثنا را درک کنید، می توانید از این راه حل ها برای پیش بینی بیشتر جریان برنامه های خود استفاده کنید.