Danh sách bài viết

Bài 8: Vòng lặp for và hàm range

Học vòng lặp for trong Python: cú pháp for ... in iterable, hàm range(), lặp qua string / list / dict, enumerate, zip, break, continue, for ... else.

24/05/2026
11 phút đọc
0 lượt xem
1

Mục tiêu bài học

Sau bài này bạn sẽ:

  • Hiểu vì sao cần vòng lặp và bản chất for trong Python là lặp qua iterable.
  • Dùng được range() với 1, 2, 3 tham số và biết range là lazy.
  • Lặp qua str, list, dict.
  • Dùng enumerate()zip() đúng tình huống.
  • Phân biệt breakcontinue; biết khối for ... else chạy khi nào.
2

Vì sao cần vòng lặp

Nếu cần in từ 1 đến 5, bạn có thể viết:

print(1)
print(2)
print(3)
print(4)
print(5)

Nhưng nếu là 1 đến 1000? Hay 1 đến 1 triệu (kích cỡ một dataset nhỏ)? Lặp tay không khả thi. Vòng lặp giúp chạy cùng một khối lệnh nhiều lần, mỗi lần với một giá trị khác.

Trong AI / ML, vòng lặp xuất hiện ở khắp nơi: duyệt từng sample trong dataset, duyệt từng epoch khi train model, duyệt từng token trong câu, duyệt từng layer của neural network. Hiểu chắc for là điều kiện đủ để đọc được phần lớn code Python trong các bài sau.

3

Cú pháp for ... in iterable

Cú pháp tổng quát:

for <biến> in <iterable>:
    <khối lệnh thân vòng lặp>

Iterable là một đối tượng mà Python có thể duyệt qua từng phần tử. Ví dụ: str, list, tuple, dict, set, range, file object. Mỗi vòng lặp, Python lấy phần tử kế tiếp gán cho biến rồi chạy thân vòng lặp.

# Lặp qua một list
for n in [10, 20, 30]:
    print(n)
# 10
# 20
# 30

Lưu ý quan trọng: for trong Python không phải counter loop như for (i = 0; i < n; i++) trong C / Java. Python không có biến đếm ẩn — bạn chỉ "rút" từng phần tử từ iterable. Muốn có chỉ số, dùng range() hoặc enumerate() (xem mục 4 và 7).

Như mọi khối lệnh khác trong Python, thân vòng lặp thụt vào bằng 4 spaces:

for n in [1, 2, 3]:
    square = n * n          # thân vòng lặp
    print(n, "->", square)  # thân vòng lặp
print("Xong")               # KHÔNG còn trong vòng lặp
4

Hàm range()

range() sinh ra một dãy số nguyên — chính là cách quen thuộc để chạy vòng lặp "n lần". Có 3 dạng:

4.1. range(stop) — từ 0 đến stop-1

for i in range(5):
    print(i)
# 0
# 1
# 2
# 3
# 4

Chú ý: không bao gồm stop. range(5) sinh 5 số: 0, 1, 2, 3, 4.

4.2. range(start, stop) — từ start đến stop-1

for i in range(2, 6):
    print(i)
# 2
# 3
# 4
# 5

4.3. range(start, stop, step) — bước nhảy step

# Số chẵn từ 0 đến 10
for i in range(0, 11, 2):
    print(i)
# 0, 2, 4, 6, 8, 10

# step âm để đếm ngược
for i in range(10, 0, -1):
    print(i)
# 10, 9, 8, 7, 6, 5, 4, 3, 2, 1

step bằng 0 sẽ báo ValueError. step âm thì start phải lớn hơn stop, nếu không vòng lặp rỗng.

4.4. range là lazy

range() không sinh ngay toàn bộ dãy số ra bộ nhớ. Nó chỉ giữ lại start, stop, step và tính phần tử kế tiếp khi cần:

r = range(10_000_000)
print(r)            # range(0, 10000000)  — không in ra 10 triệu số
print(type(r))      # <class 'range'>
print(len(r))       # 10000000  — tính trong O(1)

# Muốn có list thật sự thì ép kiểu
print(list(range(5)))   # [0, 1, 2, 3, 4]

Đặc tính lazy này giúp for i in range(10_000_000) chạy được mà không tốn ~80MB RAM cho list 10 triệu phần tử.

5

Lặp qua string

Chuỗi cũng là iterable. Lặp qua chuỗi sẽ trả về từng ký tự:

for ch in "AI":
    print(ch)
# A
# I

Dùng để đếm ký tự đặc biệt, ví dụ đếm nguyên âm:

text = "machine learning"
vowels = "aeiou"
count = 0
for ch in text:
    if ch in vowels:
        count += 1
print(count)   # 6
6

Lặp qua list và dictionary

List và dict sẽ được học chi tiết ở bài 11 và bài 13. Ở đây chỉ cần biết cú pháp lặp:

6.1. Lặp qua list

scores = [8.5, 7.0, 9.2, 6.0]
for s in scores:
    print(s)

6.2. Lặp qua dictionary

Mặc định lặp qua dict sẽ trả về các key:

person = {"name": "Alice", "age": 25, "role": "AI Engineer"}

for key in person:
    print(key)
# name
# age
# role

Muốn cả key và value, dùng .items():

for k, v in person.items():
    print(k, "->", v)
# name -> Alice
# age -> 25
# role -> AI Engineer

Hai phương thức bổ sung: .keys() (chỉ key, tương đương lặp mặc định) và .values() (chỉ value).

7

enumerate — lấy cả index và value

Khi vừa cần phần tử vừa cần chỉ số, đừng tự tăng biến đếm thủ công. Dùng enumerate():

labels = ["cat", "dog", "bird"]

for i, label in enumerate(labels):
    print(i, label)
# 0 cat
# 1 dog
# 2 bird

Có thể truyền tham số start để bắt đầu chỉ số từ một giá trị khác:

for i, label in enumerate(labels, start=1):
    print(i, label)
# 1 cat
# 2 dog
# 3 bird

So sánh với cách viết không khuyến nghị:

# Không khuyến nghị — dài và dễ sai
for i in range(len(labels)):
    print(i, labels[i])

# Khuyến nghị — gọn và rõ
for i, label in enumerate(labels):
    print(i, label)
8

zip — lặp song song nhiều iterable

zip() ghép từng phần tử cùng vị trí của nhiều iterable thành một tuple:

names = ["Alice", "Bob", "Charlie"]
ages  = [25, 30, 28]

for name, age in zip(names, ages):
    print(name, age)
# Alice 25
# Bob 30
# Charlie 28

Tình huống AI / ML quen thuộc: lặp song song featurelabel:

features = [[1.2, 0.5], [0.7, 1.8], [2.1, 0.3]]
labels   = [0, 1, 0]

for x, y in zip(features, labels):
    print("input:", x, "label:", y)

Nếu các iterable không cùng độ dài, zip() dừng ở iterable ngắn nhất (các phần tử dư của iterable dài hơn bị bỏ):

a = [1, 2, 3, 4]
b = ["x", "y"]
print(list(zip(a, b)))   # [(1, 'x'), (2, 'y')]

Muốn lỗi rõ ràng khi độ dài khác nhau, từ Python 3.10 có zip(a, b, strict=True) — sẽ báo ValueError.

9

break và continue

Đôi khi cần thoát vòng lặp sớm hoặc bỏ qua một số phần tử.

9.1. break — thoát vòng lặp ngay lập tức

# Tìm số đầu tiên chia hết cho 7 trong dãy 1..100
for i in range(1, 101):
    if i % 7 == 0:
        print("Tìm thấy:", i)
        break    # thoát ngay, không duyệt nữa

9.2. continue — bỏ qua iteration hiện tại, chạy tiếp

# In các số lẻ từ 1 đến 10
for i in range(1, 11):
    if i % 2 == 0:
        continue     # nếu chẵn, bỏ qua phần còn lại
    print(i)
# 1
# 3
# 5
# 7
# 9

Cả hai chỉ áp dụng cho vòng lặp gần nhất bao quanh — nếu có lặp lồng nhau, break chỉ thoát lặp trong cùng.

10

for ... else

Python cho phép gắn một khối else sau for. Khối này chạy chỉ khi vòng lặp kết thúc bình thường (duyệt hết iterable), không chạy nếu thoát bằng break.

# Tìm số chia hết cho 13 trong 1..10
for i in range(1, 11):
    if i % 13 == 0:
        print("Found:", i)
        break
else:
    print("Không có số nào chia hết cho 13.")
# Output: Không có số nào chia hết cho 13.

Pattern này hữu ích cho các bài toán "tìm kiếm" — không cần biến cờ (flag) phụ. Tuy nhiên cú pháp dễ gây nhầm với if ... else, nên dùng có chừng mực và viết comment khi cần.

11

Bài tập

Bài 1: Tính tổng các số nguyên từ 1 đến 100 bằng vòng lặp for. Đáp số đúng là 5050.

Gợi ý: khởi tạo total = 0 trước vòng lặp, mỗi iteration cộng thêm i.

Bài 2: Cho list:

numbers = [3, 8, 12, 5, 7, 14, 9, 22, 1, 6]

Viết vòng lặp in ra tất cả số chẵn trong list. Sau đó in ra tổng các số chẵn.

Bài 3: Cho hai list cùng độ dài:

products  = ["sách", "bút", "vở"]
prices    = [55000, 8000, 12000]

Dùng zip()enumerate() để in ra:

1. sách - 55000 VND
2. bút - 8000 VND
3. vở - 12000 VND

Bài 4: Viết vòng lặp duyệt từ 1 đến 30, dừng (break) ngay khi gặp số đầu tiên chia hết cho cả 3 và 5. In ra số đó.

12

Tóm tắt

  • for <var> in <iterable>: duyệt qua từng phần tử của iterable, không phải counter loop.
  • range(stop), range(start, stop), range(start, stop, step) — sinh dãy số nguyên, không bao gồm stop; step có thể âm.
  • range là lazy: không sinh list ngay, chỉ tính phần tử khi cần.
  • Lặp qua str trả về từng ký tự; lặp qua list trả về từng phần tử; lặp qua dict mặc định trả về key, dùng .items() để lấy cả key và value.
  • enumerate(iterable): lấy đồng thời index và value, ưu tiên thay vì range(len(...)).
  • zip(a, b, ...): lặp song song nhiều iterable, dừng ở iterable ngắn nhất; thêm strict=True để ép cùng độ dài (Python 3.10+).
  • break thoát vòng lặp; continue bỏ qua iteration hiện tại; for ... else chạy else khi loop kết thúc mà không gặp break.

Bài tiếp theo sẽ học vòng lặp while — lặp khi điều kiện còn đúng, và sâu hơn về break / continue.