TL;DR
Số giá trị trong iterable nhiều hơn số biến ở vế trái của phép gán. Hãy thêm biến cho khớp, dùng *rest để hứng phần dư, hoặc kiểm tra lại nguồn dữ liệu đang unpack.
# Lỗi
a, b = (1, 2, 3) # ValueError: too many values to unpack (expected 2)
# Sửa — khớp số lượng
a, b, c = (1, 2, 3)
# Sửa — thu thập phần dư bằng star
a, b, *rest = (1, 2, 3, 4, 5) # rest = [3, 4, 5]
Nguyên nhân
Unpacking trong Python hoạt động theo vị trí. Số biến ở vế trái phải khớp chính xác với số giá trị ở vế phải. Chỉ cần thừa một giá trị là đủ để kích hoạt ValueError: too many values to unpack (expected N).
Bốn tình huống hay gặp nhất:
- Gán tuple hoặc list cho ít biến hơn số phần tử nó chứa
- Duyệt dictionary mà không dùng
.items()— mỗi key bị xem là iterable của các ký tự - Unpack các dòng CSV hoặc response API có thêm trường chưa được tính đến
- Dùng
for a, b in some_listkhi mỗi phần tử có nhiều hơn 2 mục
Cách sửa 1 — Khớp số lượng biến
Đếm số giá trị rồi khai báo đúng số biến tương ứng. Dễ thấy với literal, nhưng dễ bỏ sót khi dữ liệu đến từ một hàm gọi thay vì do chính bạn viết ra.
# Sai
first, last = "John", "Paul", "George", "Ringo"
# Đúng
first, second, third, fourth = "John", "Paul", "George", "Ringo"
Phù hợp với cấu trúc cố định khi bạn luôn biết trước số lượng chính xác.
Cách sửa 2 — Dùng cú pháp Star (*) để bắt tất cả phần còn lại
Chỉ cần một số vị trí nhất định? Dùng * để hứng phần còn lại vào một list:
# Lấy phần tử đầu và cuối, thu thập phần giữa
first, *middle, last = [10, 20, 30, 40, 50]
print(first) # 10
print(last) # 50
print(middle) # [20, 30, 40]
# Lấy hai phần tử đầu, bỏ qua phần còn lại
a, b, *_ = get_csv_row() # _ là quy ước bỏ qua giá trị
Cú pháp star xử lý dữ liệu có độ dài thay đổi rất gọn — dòng CSV, payload API, bất kỳ nguồn nào có thể xuất hiện thêm trường mà không báo trước. Pattern *_ là cách viết Python thông dụng với nghĩa "tôi biết còn dữ liệu nhưng không cần dùng."
Cách sửa 3 — Duyệt Dictionary mà không có .items()
Cái bẫy này hại rất nhiều người. Duyệt dict mà không dùng .items() chỉ cho bạn các key, không phải cặp (key, value). Nếu key là chuỗi như "name", Python thấy 4 ký tự riêng lẻ — unpack vào hai biến là thất bại ngay:
# Lỗi — duyệt key, không phải cặp (key, value)
data = {"name": "Alice", "age": 30}
for key, value in data: # ValueError: "name" có 4 ký tự, không phải 2
print(key, value)
# Sửa
for key, value in data.items():
print(key, value)
# name Alice
# age 30
Dùng .items() để lấy cặp key-value, .keys() chỉ lấy key, .values() chỉ lấy value.
Cách sửa 4 — Unpack trong vòng lặp
Mỗi phần tử trong list có 3 trường nhưng bạn chỉ unpack 2. Python không thể tự đoán trường nào cần bỏ:
# Dữ liệu có 3 trường mỗi dòng
records = [
("alice", "alice@example.com", "admin"),
("bob", "bob@example.com", "user"),
]
# Lỗi — chỉ có 2 biến cho 3 giá trị
for name, email in records: # ValueError
print(name, email)
# Sửa — thêm biến thứ ba
for name, email, role in records:
print(name, email, role)
# Sửa — dùng star nếu không cần role
for name, email, *_ in records:
print(name, email)
Cách sửa 5 — Unpack giá trị trả về từ hàm
Giá trị trả về có thể thay đổi theo thời gian. Một hàm từng trả về 2 giá trị được thêm vào trường thứ ba về sau — timestamp, status code, refresh rate. Mọi nơi unpack đúng 2 biến sẽ bị lỗi âm thầm cho đến lúc chạy thật:
def get_dimensions():
return 1920, 1080, 144 # thêm refresh rate về sau
# Lỗi
width, height = get_dimensions() # ValueError
# Sửa
width, height, refresh = get_dimensions()
# Nếu thực sự không cần giá trị thừa
width, height, *_ = get_dimensions()
Nếu bạn là chủ của hàm đó, hãy coi việc thêm giá trị trả về là một thay đổi phá vỡ tương thích. Ghi lại tài liệu rõ ràng. Người dùng hàm cần được biết.
Cách sửa 6 — Phân tích CSV / File
File CSV đặc biệt dễ gặp lỗi này. Dấu phẩy thừa cuối dòng, trường tùy chọn, thay đổi schema — tất cả đều sinh ra các dòng có nhiều cột hơn bạn mong đợi:
import csv
# Lỗi nếu bất kỳ dòng nào có nhiều hơn 2 cột
with open("data.csv") as f:
reader = csv.reader(f)
for row in reader:
name, score = row # ValueError ở các dòng có thêm cột
# Sửa — truy cập theo chỉ số
with open("data.csv") as f:
reader = csv.reader(f)
for row in reader:
name = row[0]
score = row[1]
# Tốt hơn — dùng DictReader cho cột có tên
with open("data.csv") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], row["score"])
Cách xác nhận đã sửa xong
Không còn ValueError ở lần chạy tiếp theo — đó là xác nhận. Với nguồn dữ liệu bên ngoài như API, file, hoặc database, hãy in một mẫu thô trước khi viết logic unpack:
# Kiểm tra cấu trúc trước khi unpack
data = some_function()
print(type(data), len(data), data)
# Sau đó unpack với sự tự tin
a, b, c = data
Một lệnh print ở đầu giúp tiết kiệm mười phút debug về sau.
Danh sách kiểm tra nhanh
- Đếm giá trị so với biến — phải khớp, hoặc dùng
*rest - Đang duyệt dict? Thêm
.items()để lấy cặp key-value - Unpack trong vòng lặp
for a, b in list? Kiểm tra độ dài thực của từng phần tử trước - Phân tích CSV hoặc JSON? Cột có thể đã thay đổi — in một dòng thô trước khi unpack
- Gọi hàm trả về tuple? Xác nhận số giá trị trả về chưa tăng lên

