Python KeyError Exceptions and How to Handle Them (Translated)
Bài đăng này đã không được cập nhật trong 5 năm
KeyError
là một exception rất quen thuộc với những người mới bắt đầu tiếp xúc với Python. Biết tại sao KeyError
được raise lên và một số giải pháp giúp chương trình của bạn không bị dừng lại là một điều thật tuyệt với lập trình viên Python.
By the end of this tutorial, you'll know:
- Ý nghĩa của
KeyError
- Bạn có thể bắt gặp
KeyError
trong một thư viện chuẩn - Cách xử lý
KeyError
khi bạn gặp nó
What a Python KeyError
Usually Means
Exception KeyError
được raise lên khi bạn cố gắng truy cập một key không hề tồn tại trong một dictionary (dict).
Document chuẩn của Python nói rằng KeyError
được raise lên khi việc tham chiếu key (mapping key) được thực hiện và không được tìm thấy trong ánh xạ (mapping). Ánh xạ là một cấu trúc dữ liệu kết nối một tập giá trị A với một tập giá trị khác B. Ánh xạ phổ biến nhất trong Python chính là dictionary.
KeyError
là một exception thuộc kiểu LookupError
chỉ ra lỗi khi tìm kiếm key. Khi bạn thấy KeyError
, điều đó có nghĩa là key đang được tìm kiếm có thể không tồn tại.
Ở trong ví dụ dưới đây, bạn có thể thấy một dict (ages
) được khai báo với tuổi của ba người. Khi bạn cố gắng truy cập một key không thuộc về dict đó, exception KeyError
sẽ được raise lên:
>>> ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
>>> ages['Michael']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Michael'
Trong chương trình bên dưới, bạn có thể thấy dict ages
được khai báo lại một lần nữa. Lần này, bạn sẽ được gợi ý cung cấp tên của một người và lấy ra tuổi của người đó:
# ages.py
ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
person = input('Get age for: ')
print(f'{person} is {ages[person]} years old.')
Lặp lại lỗi trước đó, chúng ta nhận được traceback khác:
$ 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'
Where Else You Might See a Python KeyError
in the Standard Library
Phần lớn thời gian, KeyError
được raise lên khi một key không được tìm thấy trong một dict và một class con của dict (ví dụ như os.environ
)
Hiếm hoi hơn, bạn có thể thấy nó được raise lên ở một thư viện chuẩn của Python, ví dụ như ở trong module zipfile
nếu như một file không được tìm thấy trong một tập nén zip.
Chúng ta sẽ sử dụng class zipfile.ZipFile
để lấy ra các thông tin về một tập nén zip với .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"
Rõ ràng, KeyError
ở đây không hề tồn tại trong code của bạn. Nó thuộc về thư viện zipfile
nhưng traceback đã chỉ ra vị trí lỗi trong code của bạn.
When You Need to Raise a Python KeyError
in Your Own Code
Đôi khi bạn muốn tự raise lên exception. Việc này có thể được thực hiện bằng cách sử dụng keyword raise
và gọi exception KeyError
:
raise KeyError(message)
Thông thường, message
sẽ là key cần tìm. Tuy nhiên, như trong trường họp của zipfile
, bạn có thể đưa ra nhiều thông tin hơn một chút để người đọc có thể hiểu đoạn code của bạn hơn.
Nếu bạn quyết định raise KeyError
trong code của bạn, hãy chắc chắn rằng mục đích của việc bạn làm đúng với ý nghĩa thực sự đằng sau exception này. Nghĩa là bạn phải chắc chắn rằng là exception này có thể xảy ra.
How to Handle a Python KeyError
When You See It
Khi bạn gặp exception này, có một vài cách để xử lý. Tùy trường hợp, cách này sẽ tốt hơn cách khác. Mục đích cuối cùng là không để cho các exception không mong muốn bị raise lên.
The Usual Solution: .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.")
Hàm .get()
sẽ trả về giá trị được tìm thấy trong dict đối với key truyền vào hoặc giá trị mặc định None
.
Lần này, bạn sẽ không nhận được exception KeyError
:
$ python ages.py
Get age for: Michael
Michael's age is unknown.
Bạn cũng có thể set giá trị trả về mặc định của .get()
như sau:
age = ages.get(person, 0)
Ở đây, với key truyền vào là 'Michael'
, thay vì trả về None
, nó sẽ trả về 0
.
The Rare Solution: Checking for Keys
Đôi khi bạn lại cần xác định sự tồn tại của một key, Trong những trường họp này, sử dụng .get()
sẽ không đáp ứng được yêu cầu của bạn. Việc nhận được giá trị None
khi gọi .get()
có thể rơi vào hai trường hợp: key không tồn tại hoặc key tồn tại nhưng giá trị tương ứng với nó lại chính là None
.
Với một dict hoặc đối tượng giống dict, bạn có thể sử dụng toán tử in
để xác định xem một key có nằm trong ánh xạ hay không. Toán tử này sẽ trả về một giá trị boolean (True
hoặc False
).
Trong ví dụ này, bạn nhận được một dict tên là response
sau khi gọi một API. Và response
có thể chứa key error
được dùng để chỉ ra rằng API đó trả về lỗi:
# 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
The General Solution: try except
Như với bất kỳ exception nào, bạn luôn có thể sử dụng khối try except
để tách biệt các đoạn code có thể raise lên exception và chuẩn bị một giải pháp dự phòng.
Bạn có thể sử dụng khối try except
trong ví dụ tương tự bên trên:
# 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.")
Bạn có thể thấy được ở đây trường hợp bình thường sẽ in ra tên và tuổi của một người; còn trường hợp dự phòng sẽ in ra một message khác.
Sử dụng try except
cũng là một giải pháp tuyệt vời khi .get()
hoặc toán tử in
không được hỗ trợ:
>>> 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"
All rights reserved