Lỗi Gặp Phải
Bạn đang lặp qua một thứ gì đó — kết quả từ hàm, một hàng trong database, một phản hồi API — và Python ném ra:
TypeError: 'NoneType' object is not iterable
Dịch ra: bạn đã cố lặp qua None. Mọi cơ chế lặp trong Python — vòng for, list comprehension, tuple unpacking, toán tử * spread — đều cần một iterable thực sự. None không phải vậy.
Đây là cách đơn giản nhất để kích hoạt lỗi này:
def get_users():
pass # forgot to return anything
for user in get_users(): # TypeError here
print(user)
Nguyên Nhân Gốc Rễ
Bất kỳ hàm Python nào không có câu lệnh return tường minh đều âm thầm trả về None. Đó là đặc tả ngôn ngữ, không phải lỗi. Nhưng một số pattern khác cũng tạo ra cái bẫy tương tự:
- Một hàm trả về
Noneở một nhánh bạn không để ý tới — ví dụ mộtifkhông cóelsetương ứng - Các phương thức in-place như
list.sort()haydict.update()thay đổi đối tượng và luôn luôn trả vềNone - Một truy vấn ORM, con trỏ database, hoặc lời gọi API trả về
Nonekhi tập kết quả rỗng - Bạn gán kết quả trả về của một trong các phương thức in-place đó vào một biến
Cái cuối cùng liên tục làm các lập trình viên vấp ngã. Đây là hình dạng của nó:
items = [3, 1, 2]
items = items.sort() # sort() returns None, not the sorted list!
for item in items: # TypeError: 'NoneType' object is not iterable
print(item)
items lúc này là None. Danh sách gốc đã được sắp xếp — nhưng bạn đã vứt bỏ tham chiếu đến nó.
Cách Khắc Phục
Cách 1: Kiểm tra xem hàm thực sự trả về gì
Trước khi lặp, hãy in hoặc kiểm tra giá trị:
result = get_users()
print(type(result), result) # Debug: see what you actually got
Nếu đầu ra là <class 'NoneType'> None, hàm không trả về thứ bạn mong đợi. Tìm nó và thêm câu lệnh return:
def get_users():
users = db.query("SELECT * FROM users")
return users # was missing before
Cách 2: Bảo vệ bằng kiểm tra None tường minh
Đôi khi None là kết quả hợp lệ — không tìm thấy kết quả, không có gì để xử lý. Hãy kiểm tra trước khi lặp:
users = get_users()
if users is not None:
for user in users:
print(user)
Hoặc gộp lại thành một dòng:
for user in (users or []):
print(user)
Phần or [] thay thế bằng danh sách rỗng khi users là None. Vòng lặp chạy không lần nào và không có ngoại lệ nào được ném ra.
Cách 3: Mặc định trả về collection rỗng bên trong hàm
Sửa từng chỗ gọi hàm rất mệt mỏi. Thay vào đó, hãy sửa hàm một lần duy nhất:
def get_users():
result = db.query("SELECT * FROM users")
if result is None:
return [] # always return a list
return result
Các nơi gọi hàm giờ có thể lặp tự do. Không cần kiểm tra ở mỗi chỗ sử dụng.
Cách 4: Khắc phục bẫy gán kết quả phương thức in-place
list.sort(), list.reverse(), dict.update(), và set.add() đều thay đổi in-place. Chúng trả về None. Đừng gán kết quả của chúng:
# Wrong
items = items.sort()
# Correct: sort in place, keep the reference
items.sort()
# Or use sorted() — it returns a new list
items = sorted(items)
Cách 5: Xử lý None từ các lời gọi nối chuỗi
Gọi phương thức theo chuỗi che giấu None cho đến khi nó bùng phát. Đây là cách viết rủi ro:
# response.json() might return None
for item in response.json().get("results"):
process(item)
Hãy tách ra và kiểm tra từng bước:
data = response.json()
results = data.get("results") if data else []
for item in (results or []):
process(item)
Cách 6: Toán tử walrus (Python 3.8+)
Khi hàm là từ bên ngoài và bạn không thể thay đổi kiểu trả về của nó, toán tử walrus gọn hơn so với kiểm tra hai dòng:
if (users := get_users()) is not None:
for user in users:
print(user)
Tìm Nguồn Gốc Của None
Đọc toàn bộ traceback. Python chỉ ra chính xác dòng bị lỗi:
Traceback (most recent call last):
File "app.py", line 12, in process_orders
for order in get_orders(user_id):
TypeError: 'NoneType' object is not iterable
Đến dòng 12. Iterable là get_orders(user_id). Thêm một lệnh in debug phía trên:
result = get_orders(user_id)
print(f"get_orders returned: {result!r}")
for order in result:
...
Định dạng !r gọi repr() trên giá trị. Nó phân biệt rõ ràng None với chuỗi rỗng '' hay danh sách rỗng [] — cả ba trông khác nhau trong đầu ra repr.
Kiểm Chứng
Chạy lại code sau khi sửa. Không có TypeError nghĩa là đã thành công. Để xác nhận nhánh kiểm tra xử lý None đúng cách, hãy mô phỏng tường minh:
def get_users():
return None # simulate no results
users = get_users()
for user in (users or []):
print(user)
print("Done — no error")
Đầu ra mong đợi:
Done — no error
Phần thân vòng lặp bị bỏ qua. Không có ngoại lệ. Đó là hành vi bạn muốn.
Phòng Ngừa
- Thêm type hint:
def get_users() -> list[User]:ghi lại kiểu trả về mong đợi. Chạy mypy và nó sẽ đánh dấu bất kỳ đường code nào trả vềNonethay vì đó. - Trung thực với
Optional: Nếu một hàm có thể hợp lệ trả vềNone, hãy chú thích nó làOptional[list]. Điều đó báo hiệu cho người gọi: hãy kiểm tra trước khi lặp. - Không bao giờ gán kết quả phương thức in-place:
sort(),reverse(),update(),append()— tất cả đều trả vềNone. Hãy khắc cốt ghi tâm điều này. - Trả về collection rỗng, không phải None: Một hàm lấy danh sách nên trả về
[]khi kết quả rỗng, không phảiNone. Hãy dànhNonecho trường hợp "giá trị này không tồn tại" — không phải "tôi không tìm thấy gì."

