Chuyển đến nội dung chính

Ngoại lệ KeyError trong Python và cách khắc phục

Tìm hiểu các kỹ thuật then chốt như xử lý ngoại lệ và phòng ngừa lỗi để xử lý hiệu quả ngoại lệ KeyError trong Python.
· 6 phút đọc

Khám phá cùng AI

Ngay cả những lập trình viên dày dạn nhất cũng mắc lỗi khi viết mã. Với lập trình viên Python, các lỗi này thường rơi vào hai nhóm: lỗi (error) hoặc ngoại lệ (exception).

Lỗi chủ yếu liên quan đến cú pháp và phải được sửa thì chương trình mới chạy. Ngoại lệ, ngược lại, xảy ra khi mã đúng về mặt cú pháp nhưng có điều gì đó làm gián đoạn chương trình khi đang chạy. 

Trong hướng dẫn này, chúng ta sẽ xem xét một trong những ngoại lệ phổ biến nhất mà cả người mới lẫn người có kinh nghiệm thường gặp: ngoại lệ KeyError. Chúng ta sẽ đi vào chi tiết về KeyError của Python, kèm ví dụ và các kỹ thuật khác nhau để xử lý kiểu ngoại lệ này. Nếu bạn muốn học cách nhận diện và sửa lỗi trong Python, hãy xem thêm hướng dẫn Xử lý ngoại lệ & lỗi trong Python. 

Ngoại lệ KeyError là gì?

Trong Python, ngoại lệ KeyError là một trong các ngoại lệ dựng sẵn được thiết kế để xử lý nhiều tình huống lỗi phổ biến. Có thể hiểu KeyError là một lớp con, cùng với IndexError, của ngoại lệ tổng quát hơn là LookupError; tất cả đều là đối tượng của lớp Exception.

Cụ thể, KeyError được ném ra khi lập trình viên cố truy cập một khóa không tồn tại trong từ điển (dictionary). Từ điển, để tham chiếu, là cấu trúc dữ liệu lưu trữ dữ liệu theo cặp khóa–giá trị, và giá trị được truy cập thông qua khóa — vì vậy mới có tên KeyError. 

Nguyên nhân thường gặp và ví dụ về KeyError trong Python

Hãy xem một ví dụ với một từ điển chứa các quốc gia và thủ đô của chúng:

dictionary_capitals = {'Madrid': 'Spain',
 'Lisboa': 'Portugal', 'London': 'United Kingdom'}

Để tra cứu thông tin trong từ điển, chúng ta cần chỉ định khóa trong dấu ngoặc vuông và Python sẽ trả về giá trị tương ứng.

dictionary_capitals['Madrid']
'Spain'

Nếu chúng ta cố truy cập một khóa không có trong từ điển, Python sẽ đưa ra thông báo lỗi ngoại lệ KeyError. 

dictionary_capitals['Rome']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Rome'

Chúng ta cũng có thể gặp ngoại lệ KeyError khi cố truy cập các khóa không tồn tại trong những đối tượng ánh xạ (mapping) khác của Python có dạng như một từ điển. Ví dụ, đối tượng os.environ trả về một từ điển lưu các biến môi trường của người dùng làm khóa kèm giá trị tương ứng.

Trong đoạn mã sau, chúng ta thử truy cập giá trị gắn với khóa USERS trong từ điển os.environ. Vì USERS không có trong os.environ, mã của chúng ta sinh ra KeyError.

#Calling a non-existent environmental variable
os.environ['USERS']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'USERS'

Hóa ra, từ điển rất phổ biến trong Python. Một ví dụ khác là cách triển khai một khái niệm trừu tượng hơn gọi là hashmap. Hãy xem hướng dẫn Cẩm nang về Hashmap trong Python để tìm hiểu thêm.

Xử lý ngoại lệ KeyError trong Python

Hãy cùng khám phá các phương pháp xử lý KeyError. Có hai chiến lược: hoặc phòng ngừa KeyError, hoặc bắt KeyError bằng cơ chế xử lý lỗi. 

Phòng ngừa KeyError

Như đã thấy, Python sẽ ném KeyError nếu chúng ta cố truy cập một khóa không tồn tại. Để ngăn điều này, ta có thể truy cập khóa trong từ điển bằng phương thức .get() để mã khoan dung hơn. Khi dùng phương thức này, nếu gặp khóa không tồn tại, Python sẽ trả về giá trị None thay vì KeyError. Đây là một cách tiếp cận tốt.

print(dictionary_capitals.get('Prague'))
None

Ngoài ra, chúng ta có thể kiểm tra khóa có tồn tại hay không trước khi truy cập. Cách phòng ngừa ngoại lệ này được gọi là “Nhìn trước khi nhảy” (Look Before You Leap — LBYL). Trong trường hợp này, ta có thể dùng câu lệnh if để kiểm tra sự tồn tại của khóa và nếu không có, ta xử lý trong mệnh đề else.

capital = "Prague"
if capital in dictionary_capitals.keys():
    value = dictionary_capitals[capital]
else:
    print("The key {} is not present in the dictionary".format(capital)) 

Bắt KeyError bằng xử lý ngoại lệ

Cách tiếp cận thứ hai được gọi là “Xin tha thứ dễ hơn xin phép” (Easier to Ask Forgiveness Than Permission — EAFP) và thực ra là cách chuẩn hơn để xử lý ngoại lệ trong Python. 

Theo phong cách EAFP, ta giả định các khóa hợp lệ tồn tại và bắt ngoại lệ nếu giả định đó sai. Trong khi LBYL dựa vào các câu lệnh if/else, EAFP dựa vào các khối try/except. 

Trong ví dụ sau, thay vì kiểm tra khóa có tồn tại, ta thử truy cập khóa mong muốn. Nếu vì lý do nào đó khóa không có, ta chỉ cần bắt KeyError trong khối except và xử lý phù hợp.

capital = "Prague"
try:
     value = dictionary_capitals[capital]
except KeyError:
     print("The key {} is not present in the dictionary".format(capital)) 

Quản lý KeyError nâng cao trong Python

Dùng defaultdict để xử lý khóa tự động

Ta thấy rằng mỗi khi cố truy cập một khóa không có trong từ điển, Python sẽ trả về ngoại lệ KeyError. Phương thức .get() mà ta đã xem là một cách tiếp cận khoan dung với lỗi và hoạt động ổn, nhưng chưa tối ưu.

Mô-đun Collections cung cấp defaultdict, một cách tối ưu hơn để xử lý các mục trong từ điển. Khác với từ điển chuẩn sẽ ném KeyError khi truy cập khóa không tồn tại, defaultdict cho phép ta chỉ định một giá trị mặc định được trả về bất cứ khi nào không tìm thấy khóa. Đoạn mã sau cho thấy cách import defaultdict từ Collections và tạo giá trị mặc định bằng hàm lambda. Nếu bạn còn bối rối về hàm lambda, hãy xem hướng dẫn Python lambda để tìm hiểu thêm.

from collections import defaultdict 

# Defining the dict 
capitals = defaultdict(lambda: "The key doesn't exist") 
capitals['Madrid'] = 'Spain'
capitals['Lisboa'] = 'Portugal'
print(capitals['Madrid']) 
print(capitals['Lisboa']) 
print(capitals['Ankara'])
Spain
Portugal
The key doesn't exist

Cố ý tạo ra KeyError

Cuối cùng, trong một số trường hợp, việc cố ý ném ra KeyError có thể phù hợp để áp đặt một số ràng buộc nhất định.

Chúng ta biết rằng trong nhiều tình huống, hàm phụ thuộc vào dữ liệu cụ thể để thực thi thao tác. Chủ động kích hoạt KeyError sẽ đảm bảo hàm thất bại một cách rõ ràng và có thể dự đoán khi dữ liệu bắt buộc bị thiếu. Trong những trường hợp này, thông điệp lỗi đi kèm KeyError giúp ta hiểu chính xác vấn đề với đầu vào, từ đó gỡ lỗi dễ hơn.

Hãy làm rõ bằng một ví dụ. Ở đây chúng ta tạo một hàm nhận một từ điển làm đối số. Người dùng phải cung cấp từ điển với các khóa phù hợp để hàm hoạt động chính xác. Chúng ta tạo hàm trong trường hợp này vì muốn biết ngay nếu thiếu khóa, thay vì để lỗi tồn tại sâu hơn trong mã. 

def my_function(my_dict): 
    if 'critical_key' not in my_dict: 
        raise KeyError('critical_key is missing from the dictionary. Please modify the dictionary')

Kết luận

Hy vọng bạn thấy bài viết hữu ích. KeyError là một trong những ngoại lệ phổ biến nhất mà lập trình viên Python sẽ gặp. Thành thạo nghệ thuật xử lý lỗi và ngoại lệ là phần then chốt để trở thành một lập trình viên Python vững vàng. Tìm hiểu thêm về các kỹ thuật lập trình hàm và các nền tảng mã hóa khác với các lộ trình kỹ năng Python Programming và Python Fundamentals. Ngoài ra, hãy xem hướng dẫn Tập hợp trong Python và lý thuyết tập hợp để tìm hiểu thêm về các khái niệm liên quan. 

Javier Canales Luna

Tác giả

Javier Canales Luna

Tôi là một chuyên viên phân tích dữ liệu tự do, hợp tác với các công ty và tổ chức trên toàn thế giới trong các dự án khoa học dữ liệu. Tôi cũng là giảng viên khoa học dữ liệu với hơn 2 năm kinh nghiệm. Tôi thường xuyên viết bài về khoa học dữ liệu bằng tiếng Anh và tiếng Tây Ban Nha; một số bài đã được đăng trên các trang uy tín như DataCamp, Towards Data Science và Analytics Vidhya. Là một nhà khoa học dữ liệu có nền tảng khoa học chính trị và luật, mục tiêu của tôi là làm việc tại giao điểm giữa chính sách công, pháp luật và công nghệ, tận dụng sức mạnh của ý tưởng để thúc đẩy các giải pháp và cách tiếp cận mới nhằm giúp chúng ta đối mặt với những thách thức cấp bách, đặc biệt là khủng hoảng khí hậu. Tôi xem mình là người tự học, không ngừng trau dồi và là một người ủng hộ vững chắc cho tính đa ngành. Không bao giờ là quá muộn để học điều mới.

Chủ đề

Liên quan