Python là một ngôn ngữ lập trình mạnh mẽ với cú pháp dễ hiểu cho phép bạn có thể tự học mà không cần nhiều kiến thức nền tảng về khoa học máy tính. Trong quá trình học, có thể bạn sẽ mắc phải nhiều sai lầm do thiếu hiểu biết về các concept. Học cách sửa chữa những sai lầm này sẽ tăng cường hiểu biết của bạn về các kiến thức cơ bản cũng như các kĩ năng lập trình.
Bài viết này sẽ tổng hợp một số lỗi thường gặp trong Python mà nhiều người hay mắc phải khi mới bắt đầu học và cách sửa chữa, phòng tránh.
1. Tải lại các module sau khi sửa đổi
Bạn đã bao giờ tốn hàng tiếng đồng hồ để gỡ lỗi và sửa chữa một trục trặc và cuối cùng bạn nhận ra là mình không gỡ lỗi trên đoạn mã nguồn đã được sửa đổi ? Điều này thường xảy ra đối với những người mới học khi họ không nhận ra rằng cả module chỉ được tải lên bộ nhớ một lần khi lệnh import được thực thi. Vậy nếu bạn sửa code trong một module riêng và and import vào code hiện tại của bạn, bạn sẽ phải tải lại module để phản chiếu các thay đổi mới nhất.
Để tải lại một module, bạn sẽ cần phải dùng hàm reload từ importlib module:
1 from importlib import reload
2
3 # some module which you have made changes
4 import externallib
5
6 reload(externallib)
2. Đặt tên xung đột giữa biến toàn cục và biến cục bộ
Tưởng tượng rằng bạn định nghĩa một biến toàn cục tên là app_config, và bạn muốn sử dụng nó trong hàm init_config bên dưới:
1 app_config = “app.ini”
2
3 def init_config():
4 app_config = app_config or “default.ini”
5 print(app_config)
Bạn có thể đang chờ in ra “app.ini” vì nó đã được định nghĩa trên toàn cục, nhưng ngạc nhiên thay, bạn lại gặp phải ngoại lệ “UnboundLocalError” bởi biến app_config đã được tham chiếu trước khi gán. Nếu bạn diễn giải lệnh gán và in biến ra, bạn sẽ thấy giá trị được in ra chính xác. Vậy thì điều gì đang xảy ra ở đây ?
Ngoại lệ trên xảy ra bởi Python cố tạo ra một biến trong local scope bất cứ khi nào có một biểu thức gán, và khi biến cục bộ và biến toàn cục có cùng tên, biến toàn cục sẽ bị che đi trong local scope. Và vì vậy Python sẽ báo lỗi rằng biến cục bộ app_config đang được dùng trước khi nó được khởi tạo.
Để giải quyết đặt tên xung đột, bạn phải sử dụng các cái tên khác nhau cho biến toàn cục và biến cục bộ để tránh nhầm lẫn, ví dụ :
1 app_config = “app.ini”
2
3 def init_config():
4 config = app_config or “default.ini”
5 print(config)
3. Kiểm tra các giá trị Falsy
Kiểm tra true hoặc false của một biến trong lệnh if hoặc đôi khi có thể sai sót. Những người mới học Python thường lẫn lộn giữa giá trị None và các giá trị falsy khác và cuối cùng là viết ra những đoạn code lỗi. Ví dụ: giả sử bạn muốn kiểm tra xem nếu price không phải là None và dưới 5, thì chương trình kích hoạt một vài cảnh báo selling :
1 def selling_alert(price):
2 if price and price < 5:
3 print(“selling signal!!!”)
Mọi thứ trông có vẻ ổn, nhưng khi bạn kiểm tra với price = 0, bạn sẽ không có bất kì cảnh báo nào :
1 selling_alert(0)
2 # Nothing has been printed out
Điều này là do cả None và 0 đều được Python đánh giá là False, do vậy lệnh in sẽ bị bỏ qua mặc dù price < 5 là true.
Trong python, các đối tượng dãy trống chẳng hạn như “” (empty string), list, set, dict, tuple v….v… đều được đánh giá là False, và cả số 0 trong bất kì định dạng số học nào như 0 và 0.0. Vậy để tránh gặp phải trục trặc Bạn sẽ phải làm rõ ràng khi nào logic của bạn cần phải phân biệt giữa None và các giá trị False khac và phải chia logic ra nếu cần thiết, ví dụ:
1 if price is None:
2 print(“invalid input”)
3 elif price < 5:
4 print(“selling signal!!!”)
4. Giá trị mặc định và Liên kết biến
Giá trị mặc định có thể được sử dụng khi bạn muốn làm cho tham số hàm của bạn là tùy chọn nhưng vẫn thay đổi linh hoạt. Hãy tưởng tượng bạn cần thực thi một hàm logging với một tham số event_time, bạn muốn đặt cho nó một giá trị mặc định như một dấu thời gian khi nó chưa được đặt giá trị. Bạn có thể vui vẻ viết một vài dòng code như dưới đây:
1 from datetime import datetime
2
3 def log_event_time(event, event_time=datetime.now()):
4 print(f”log this event – {event} at {event_time}”)
Và bạn sẽ cho rằng miễn là event_time không được cung cấp khi gọi hàm log_event_time, nó sẽ ghi lại một sự kiện với dấu thời gian khi hàm được gọi lên. Nhưng khi bạn kiểm tra lại:
1 log_event_time(“check-in”)
2
3 # log this event – check-in at 2021-02-21 14:00:56.046938
4
5 log_event_time(“check-out”)
6
7 # log this event – check-out at 2021-02-21 14:00:56.046938
Bạn sẽ thấy các sự kiện được ghi lại với cùng một dấu thời gian. Vậy tại sao giá trị mặc định cho event_time lại không hoạt động ?
Để trả lời cho câu hỏi này thì bạn phải biết rằng, liên kết biến diễn ra trong quá trình định nghĩa hàm. Với ví dụ trên, giá trị mặc định của event_time đã được gán khi hàm bắt đầu được định nghĩa. Và cùng một giá trị đó sẽ được sử dụng mỗi khi gọi hàm.
Để giải quyết, bạn phải gán None là giá trị mặc định và kiểm tra để ghi đè event_time trong gọi hàm khi nó là None. Ví dụ như sau:
1 def log_event_time(event, event_time=None):
2 event_time = event_time or datetime.now()
3 print(f”log this event – {event} at {event_time}”)
Lỗi liên kết biến tương tự có thể xảy ra khi bạn thực thi các hàm lambda.
5. Giá trị mặc định cho các Đối tượng biến đổi
Một lỗi sai khác mà những người mới học Python thường mắc phải là đặt giá trị mặc định cho một tham số hàm có thể biến đổi. Ví dụ như tham số user_list trong hàm add_white_list dưới đây :
1 def add_white_list(user, user_list=[]):
2 user_list.append(user)
3 return user_list
Bạn có thể cho rằng khi user_list chưa được đưa ra, một danh sách trống sẽ được tạo, sau đó một người dùng mới sẽ được thêm vào danh sách này và được trả về. Nó sẽ hoạt động như dưới đây:
1 my_list = add_white_list(‘Jack’)
2
3 # [‘Jack’]
4
5 my_list = add_white_list(‘Jill’, my_list)
6
7 #[‘Jack’, ‘Jill’]
Nhưng khi bạn muốn bắt đầu lại với một danh sách trống, thì bạn lại thấy kết quả như sau:
1 my_new_list = add_white_list(‘Joe’)
2 # [‘Jack’, ‘Jill’, ‘Joe’]
Từ ví dụ về liên kết biến trước đó, chúng ta biết rằng giá trị mặc định cho user_list được tạo ra chỉ một lần trong thời gian định nghĩa hàm Và bởi list là có thể biến đổi, các thay đổi đối với đối tượng list sẽ được tham chiếu bởi các lần gọi hàm kế tiếp.
Để giải quyết vấn đề, chúng ta sẽ gán None là giá trị mặc định cho biến user_list và sử dụng một biến cục bộ để tạo danh sách mới khi user_list không được đưa ra khi gọi. Ví dụ.:
1 def add_white_list(user, user_list=None):
2 if user_list is None:
3 user_list = []
4 user_list.append(user)
5 return user_list
Ai đó có thể nhầm lẫn rằng datetime.now() sẽ tạo ra một class instance của Python, và nó cũng có thể biến đổi. Nếu bạn kiểm tra tài liệu Python bạn sẽ thấy sự thực thi của datetime thực ra là bất biến.