Vòng lặp for
Bạn sẽ học được
- Cách
forduyệt qua list, chuỗi, dict vàrange. enumeratekhi cần cả chỉ số,zipkhi cần duyệt song song.- Mẫu cộng dồn — nền tảng của mọi phép tổng hợp dữ liệu.
- Vòng lặp lồng nhau và
for ... else. - Vì sao không nên sửa list đang duyệt.
Khái niệm cốt lõi
for trong Python duyệt qua một iterable — bất cứ thứ gì có thể lấy lần lượt từng phần tử: list, tuple, chuỗi, dict, file, range. Khác nhiều ngôn ngữ, không có biến đếm và điều kiện; Python tự lấy phần tử tiếp theo cho đến hết.
names = ["An", "Bình", "Chi"]
for name in names:
print(name)An
Bình
ChiBiến name lần lượt nhận từng phần tử. Sau vòng lặp, nó vẫn giữ giá trị cuối ("Chi") — Python không xoá biến sau for. Đừng dựa vào điều này; nếu cần giá trị cuối, hãy gán tường minh.
Chuỗi cũng là iterable, duyệt từng ký tự:
for ch in "data":
print(ch)d
a
t
arange — sinh dãy số
range không tạo list thật mà sinh giá trị lười, nên rất tiết kiệm bộ nhớ:
print(list(range(1, 6)))
print(list(range(0, 10, 3)))
print(list(range(5, 0, -1)))[1, 2, 3, 4, 5]
[0, 3, 6, 9]
[5, 4, 3, 2, 1]Cú pháp range(start, stop, step): start tính cả, stop không tính, step có thể âm. Chỉ truyền một tham số thì đó là stop và start = 0. Đây là nguồn gốc lỗi "thiếu một phần tử": range(5) cho 0..4, không phải 1..5.
Khi chỉ cần lặp đúng N lần mà không dùng số, đặt tên _:
for _ in range(3):
print("Xin chào")enumerate — chỉ số và giá trị
Không nên viết for i in range(len(names)) rồi names[i]. Dùng enumerate:
names = ["An", "Bình", "Chi"]
for i, name in enumerate(names, start=1):
print(i, name)1 An
2 Bình
3 Chistart=1 đổi điểm bắt đầu; mặc định là 0. enumerate trả cặp (chỉ số, giá trị) mỗi vòng, gọn và không cần truy cập theo chỉ số.
zip — duyệt song song
Khi có hai dãy cùng độ dài:
cities = ["Hà Nội", "Đà Nẵng", "Cần Thơ"]
sales = [120, 85, 64]
for city, amount in zip(cities, sales):
print(f"{city}: {amount} triệu")Hà Nội: 120 triệu
Đà Nẵng: 85 triệu
Cần Thơ: 64 triệuzip dừng ở dãy ngắn nhất, không báo lỗi khi lệch độ dài. Nếu cần phát hiện lệch, so len() trước hoặc dùng zip(..., strict=True) từ Python 3.10 — khi đó ném ValueError nếu độ dài khác nhau. Trong phân tích dữ liệu, lệch độ dài thường là dấu hiệu dữ liệu đã bị lọc sai, nên strict=True đáng dùng.
Mẫu cộng dồn
Đây là khuôn mẫu quan trọng nhất của chương — mọi phép tính tổng, đếm, nhóm sau này đều dựa trên nó:
sales = [120, 85, 64]
total = 0
for amount in sales:
total += amount
print(total)269Ba bước: khởi tạo biến tích luỹ trước vòng lặp, cập nhật trong vòng lặp, dùng kết quả sau vòng lặp. Nếu khởi tạo bên trong, mỗi vòng sẽ reset và kết quả chỉ còn phần tử cuối.
Các biến thể:
count = 0
for amount in sales:
if amount >= 80:
count += 1
average = total / len(sales)
print(count, average)2 89.66666666666667len(sales) bằng 0 sẽ gây ZeroDivisionError; dữ liệu thật luôn cần kiểm tra danh sách rỗng trước khi chia.
Vòng lặp lồng nhau
matrix = [[1, 2], [3, 4]]
for row in matrix:
for value in row:
print(value, end=" ")
print()1 2
3 4Vòng ngoài đi qua từng hàng, vòng trong đi qua từng phần tử. Với ma trận lớn, chi phí tăng theo tích hai kích thước; nếu chỉ cần duyệt phẳng, một vòng với hai for trong comprehension sẽ gọn hơn (bài cuối chương).
for ... else
Python có nhánh else chạy khi vòng lặp kết thúc bình thường, không bị break:
numbers = [2, 4, 6, 7]
for n in numbers:
if n % 2 == 1:
print("Tìm thấy số lẻ:", n)
break
else:
print("Toàn số chẵn")Tìm thấy số lẻ: 7Nếu list toàn số chẵn, nhánh else chạy. Cú pháp này hay gây nhầm vì else trông như "ngược lại của if", nhưng nghĩa thực là "không có break". Nhiều người thấy khó đọc và chuyển sang dùng cờ found = False. Cả hai đều chấp nhận; quan trọng là nhất quán trong toàn dự án.
Không sửa list đang duyệt
nums = [1, 2, 3, 4]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums)[1, 3]Kết quả có vẻ đúng, nhưng đây là may mắn: khi xoá phần tử, chỉ số của các phần tử sau dịch sang trái, nên vòng lặp có thể bỏ qua phần tử kế tiếp. Với [2, 4, 6], chỉ 2 bị xoá và 4 bị bỏ qua:
nums = [2, 4, 6]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums)[4]Cách đúng là tạo list mới bằng comprehension, hoặc duyệt trên bản sao for n in nums.copy():. Tạo list mới thường rõ ràng hơn:
nums = [2, 4, 6]
nums = [n for n in nums if n % 2 != 0]
print(nums)Thực hành
- In các thành phố và doanh số bằng
zip, định dạng f-string. - Dùng
enumerate(start=1)in bảng đánh số. - Tính tổng và trung bình của một list doanh số, kiểm tra list rỗng.
- Duyệt một dict và in
key = value(dict sẽ học kỹ ở bài 4, nhưngfor key in dđã chạy được). - Duyệt ma trận 3×3 và in từng hàng.
- Tái hiện lỗi xoá phần tử khi đang duyệt với
[2, 4, 6], rồi sửa bằng comprehension.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Thiếu phần tử cuối | range(1, n) không gồm n | Dùng range(1, n + 1) |
IndexError trong vòng lặp | Duyệt range(len(x)) rồi truy cập sai | Dùng for x in ... hoặc enumerate |
| Tổng luôn bằng phần tử cuối | Khởi tạo biến trong vòng lặp | Khởi tạo trước vòng lặp |
| Mất phần tử khi lọc | Sửa list đang duyệt | Tạo list mới bằng comprehension |
zip bỏ sót phần tử | Hai dãy lệch độ dài | Kiểm tra len hoặc strict=True |
ZeroDivisionError | Chia cho len của list rỗng | Kiểm tra rỗng trước |
else chạy ngoài ý muốn | Hiểu nhầm for ... else | Nhớ: else chạy khi không có break |
Tóm tắt
forduyệt iterable trực tiếp; không cần biến đếm.range(start, stop, step)không gồmstop.enumeratecho chỉ số,zipcho duyệt song song (dừng ở dãy ngắn nhất).- Mẫu cộng dồn: khởi tạo trước, cập nhật trong, dùng sau.
- Không sửa collection đang duyệt — tạo bản mới.
Use ← → to move between lessons