Python 2 — Điều khiển luồng & Vòng lặp
Python 2 — Điều khiển luồng & Vòng lặp
Một chương trình không chỉ là một dãy lệnh chạy tuần tự từ trên xuống. Sức mạnh thực sự đến từ khả năng ra quyết định (chạy đoạn này hay đoạn kia) và lặp lại (làm đi làm lại một việc). Đây chính là điều khiển luồng (control flow). Bài này đi từ những viên gạch nền tảng — biểu thức điều kiện — đến những kỹ thuật giúp bạn viết code "Pythonic": gọn, đọc dễ, và đúng tinh thần Python.
Biểu thức điều kiện & toán tử
Mọi câu lệnh điều kiện đều xoay quanh một câu hỏi: "biểu thức này đúng hay sai?". Kết quả là một giá trị kiểu bool: True hoặc False.
Toán tử so sánh
print(5 > 3) # True
print(5 == 5) # True (so sánh bằng dùng ==)
print(5 != 4) # True (khác)
print(5 >= 5) # True
print("a" < "b") # True (so sánh chuỗi theo thứ tự từ điển)
Lưu ý cực kỳ quan trọng: = là gán giá trị, còn == là so sánh. Nhầm hai cái này là lỗi kinh điển của người mới.
Python cho phép so sánh chuỗi (chaining) rất tự nhiên:
tuoi = 25
print(18 <= tuoi < 65) # True — tương đương (18 <= tuoi) and (tuoi < 65)
Toán tử logic: and / or / not
and: đúng khi cả hai vế đều đúng.or: đúng khi ít nhất một vế đúng.not: đảo ngược giá trị.
co_tien = True
co_thoi_gian = False
print(co_tien and co_thoi_gian) # False
print(co_tien or co_thoi_gian) # True
print(not co_thoi_gian) # True
Một đặc điểm tinh tế: Python dùng short-circuit (đoản mạch). Với and, nếu vế trái đã False thì vế phải không được tính nữa. Với or, nếu vế trái đã True thì dừng. Điều này hữu ích để tránh lỗi:
ten = ""
# Nếu ten rỗng, vế trái False -> không gọi ten[0], tránh IndexError
if ten and ten[0] == "A":
print("Bắt đầu bằng A")
Truthy và Falsy
Trong Python, bất kỳ giá trị nào cũng có thể được coi là đúng hoặc sai khi đặt trong ngữ cảnh điều kiện. Các giá trị falsy (bị coi là sai):
# Tất cả những giá trị sau đều "falsy":
bool(0) # False — số 0
bool(0.0) # False
bool("") # False — chuỗi rỗng
bool([]) # False — list rỗng
bool({}) # False — dict rỗng
bool(None) # False — None
Mọi thứ khác là truthy. Nhờ vậy ta viết được code rất gọn:
gio_hang = []
if gio_hang: # Pythonic: thay vì if len(gio_hang) > 0
print("Có hàng")
else:
print("Giỏ hàng trống") # dòng này chạy
if / elif / else
Cấu trúc rẽ nhánh cơ bản. Python dùng thụt lề (indentation) để xác định khối lệnh, không dùng dấu ngoặc nhọn. Quy ước là 4 dấu cách.
diem = 7.5
if diem >= 9:
xep_loai = "Xuất sắc"
elif diem >= 7:
xep_loai = "Khá"
elif diem >= 5:
xep_loai = "Trung bình"
else:
xep_loai = "Yếu"
print(xep_loai) # Khá
Cách đọc: Python kiểm tra từng điều kiện theo thứ tự. Gặp điều kiện đúng đầu tiên thì chạy khối đó rồi bỏ qua toàn bộ phần còn lại. else (tùy chọn) chạy khi không điều kiện nào đúng.
Vì thứ tự quan trọng, hãy đặt điều kiện chặt nhất lên trước. Nếu viết if diem >= 5 trước thì điểm 9.5 cũng rơi vào "Trung bình" — sai logic.
Biểu thức điều kiện một dòng (ternary)
Khi chỉ cần gán một trong hai giá trị:
tuoi = 20
trang_thai = "Người lớn" if tuoi >= 18 else "Trẻ em"
print(trang_thai) # Người lớn
Đọc như tiếng Anh: "X nếu điều kiện đúng, ngược lại Y". Dùng cho trường hợp đơn giản; đừng lồng nhiều cái vào nhau kẻo khó đọc.
Vòng lặp for
for trong Python lặp qua một iterable (vật có thể duyệt được): list, tuple, chuỗi, range, dict... Đây là khác biệt lớn so với C/Java: không có biến đếm thủ công.
Lặp với range
for i in range(5):
print(i) # in 0, 1, 2, 3, 4
range rất linh hoạt:
range(5) # 0,1,2,3,4 (stop)
range(2, 6) # 2,3,4,5 (start, stop)
range(0, 10, 2) # 0,2,4,6,8 (start, stop, step)
range(10, 0, -1) # 10,9,...,1 (đếm ngược)
Điểm cần nhớ: range(a, b) không bao gồm b. range(1, 5) cho 1,2,3,4 — bốn số, không phải năm.
Lặp trực tiếp qua iterable
Cách Pythonic nhất là duyệt thẳng phần tử, không qua chỉ số:
trai_cay = ["táo", "chuối", "cam"]
for qua in trai_cay:
print(f"Tôi thích {qua}")
Tránh kiểu for i in range(len(trai_cay)): print(trai_cay[i]) — dài và dễ lỗi off-by-one.
enumerate — khi cần cả chỉ số lẫn giá trị
trai_cay = ["táo", "chuối", "cam"]
for chi_so, qua in enumerate(trai_cay):
print(f"{chi_so}: {qua}")
# 0: táo
# 1: chuối
# 2: cam
# Muốn đếm từ 1:
for stt, qua in enumerate(trai_cay, start=1):
print(f"{stt}. {qua}")
zip — duyệt nhiều iterable song song
ten = ["An", "Bình", "Châu"]
diem = [8, 6, 9]
for t, d in zip(ten, diem):
print(f"{t} được {d} điểm")
# An được 8 điểm ...
zip dừng khi danh sách ngắn nhất hết. Rất tiện để ghép dữ liệu từ nhiều nguồn.
Lặp qua dict
gia = {"táo": 30, "chuối": 20}
for ten_qua, gia_tien in gia.items():
print(f"{ten_qua}: {gia_tien}k")
Mặc định for k in gia chỉ duyệt khóa. Dùng .items() để lấy cả khóa lẫn giá trị, .values() để lấy giá trị.
Vòng lặp while
while lặp chừng nào điều kiện còn đúng. Dùng khi không biết trước số vòng lặp.
so_du = 100
ngay = 0
while so_du > 0:
so_du -= 15 # mỗi ngày tiêu 15
ngay += 1
print(f"Hết tiền sau {ngay} ngày")
Quy tắc sống còn: bên trong while phải có gì đó làm điều kiện tiến dần tới False. Nếu quên, ta có vòng lặp vô hạn (xem phần lỗi cuối bài).
break / continue / else
Ba công cụ điều khiển luồng bên trong vòng lặp.
break: thoát ngay lập tức khỏi vòng lặp.continue: bỏ qua phần còn lại của vòng hiện tại, nhảy sang vòng kế tiếp.else(gắn với vòng lặp): chạy khi vòng lặp kết thúc bình thường (không bịbreak).
# Tìm số nguyên tố đầu tiên lớn hơn 10
for n in range(11, 50):
for d in range(2, n):
if n % d == 0:
break # n chia hết cho d -> không nguyên tố
else:
print(f"Số nguyên tố: {n}") # else của for trong: chạy khi KHÔNG break
break # break for ngoài
continue để bỏ qua phần tử không cần xử lý:
for so in range(1, 11):
if so % 2 == 0:
continue # bỏ qua số chẵn
print(so, end=" ") # 1 3 5 7 9
for...else ít gặp nhưng rất hữu ích cho bài toán "tìm kiếm":
muc_tieu = 7
day_so = [2, 4, 6, 8]
for x in day_so:
if x == muc_tieu:
print("Tìm thấy!")
break
else:
print("Không có trong danh sách") # chạy vì không break
Sơ đồ luồng
Dưới đây là luồng điển hình kết hợp điều kiện và vòng lặp:
match-case (giới thiệu)
Từ Python 3.10, có cú pháp match để so khớp mẫu — gọn hơn chuỗi if/elif dài.
def mo_ta(ma):
match ma:
case 200:
return "OK"
case 404:
return "Không tìm thấy"
case 500 | 502 | 503: # nhiều giá trị
return "Lỗi máy chủ"
case _: # _ là trường hợp mặc định
return "Không rõ"
print(mo_ta(404)) # Không tìm thấy
print(mo_ta(999)) # Không rõ
match còn so khớp được cả cấu trúc (tuple, dict, đối tượng) rất mạnh, nhưng với người mới chỉ cần nhớ: nó thay thế đẹp cho nhiều nhánh elif so sánh trên cùng một biến.
Comprehension — viết vòng lặp gọn và Pythonic
Comprehension là cách tạo list/dict/set mới từ một iterable, viết trên một dòng. Đây là một trong những thứ "Pythonic" nhất.
List comprehension
Thay vì:
binh_phuong = []
for x in range(1, 6):
binh_phuong.append(x ** 2)
# [1, 4, 9, 16, 25]
Viết gọn:
binh_phuong = [x ** 2 for x in range(1, 6)]
Cú pháp đầy đủ có thêm điều kiện lọc:
# Bình phương của các số chẵn
chan_bp = [x ** 2 for x in range(1, 11) if x % 2 == 0]
# [4, 16, 36, 64, 100]
Có thể dùng if...else trong phần biểu thức (chú ý vị trí khác với lọc):
nhan_xet = ["chẵn" if x % 2 == 0 else "lẻ" for x in range(1, 5)]
# ['lẻ', 'chẵn', 'lẻ', 'chẵn']
Dict comprehension
ten = ["An", "Bình", "Châu"]
do_dai = {t: len(t) for t in ten}
# {'An': 2, 'Bình': 4, 'Châu': 4}
Set comprehension
chu_cai = {c for c in "lap trinh python" if c != " "}
# tập hợp các ký tự khác nhau (không trùng lặp)
Khi nào dùng comprehension?
- Nên dùng khi mục tiêu là tạo ra một list/dict/set mới từ một nguồn, logic đơn giản.
- Không nên khi logic phức tạp, có nhiều câu lệnh, hoặc lồng quá sâu (2 tầng
fortrở lên thường khó đọc). Lúc đó vòngfortruyền thống rõ ràng hơn. Quy tắc vàng: nếu phải dừng lại "giải mã" comprehension, hãy viết vòng lặp thường.
Comprehension chỉ để tạo dữ liệu. Đừng dùng nó chỉ để chạy side-effect (như print); việc đó nên dùng for.
Lỗi thường gặp
Off-by-one (lệch một đơn vị)
Lỗi phổ biến nhất với vòng lặp đếm. Nhớ range(b) cho tới b-1:
# Muốn in 1 đến 5
for i in range(1, 5): # SAI: chỉ in 1,2,3,4
print(i)
for i in range(1, 6): # ĐÚNG: in 1,2,3,4,5
print(i)
Hỏi mình: "tôi cần bao gồm điểm cuối không?". Nếu có, cộng 1 vào stop.
Vòng lặp vô hạn
Xảy ra khi điều kiện while không bao giờ thành False:
# SAI: quên cập nhật i -> chạy mãi mãi
i = 0
while i < 5:
print(i)
# thiếu i += 1 !!!
# ĐÚNG:
i = 0
while i < 5:
print(i)
i += 1
Nếu lỡ chạy vòng vô hạn trong terminal, nhấn Ctrl + C để dừng.
Sửa list trong khi đang lặp qua nó
Xóa phần tử khỏi list đang duyệt gây bỏ sót:
# SAI
so = [1, 2, 3, 4]
for x in so:
if x % 2 == 0:
so.remove(x) # làm lệch chỉ số, bỏ sót phần tử
# ĐÚNG: tạo list mới bằng comprehension
so = [x for x in [1, 2, 3, 4] if x % 2 != 0]
Nhầm = và == trong if
if x = 5: # SyntaxError trong Python (may mắn là báo lỗi ngay)
if x == 5: # đúng
Tóm tắt
- Điều kiện dựa trên
bool; mọi giá trị đều có tính truthy/falsy — tận dụng để viếtif gio_hang:thay vì so sánh độ dài. - Toán tử logic
and/or/notdùng short-circuit, giúp tránh lỗi và tối ưu. if/elif/elsekiểm tra theo thứ tự; đặt điều kiện chặt nhất lên trước.forduyệt thẳng iterable; dùngenumeratekhi cần chỉ số,zipkhi duyệt song song,.items()cho dict.whilecho số vòng lặp không biết trước — luôn đảm bảo điều kiện tiến tớiFalse.break/continue/elseđiều khiển luồng tinh tế bên trong vòng lặp.match-casethay thế chuỗielifdài; comprehension tạo dữ liệu gọn và Pythonic.- Cảnh giác off-by-one, vòng lặp vô hạn, và việc sửa list khi đang lặp.
Tự kiểm tra
- Những giá trị nào trong Python được coi là "falsy"? Hãy kể ít nhất bốn.
range(2, 10, 3)sinh ra dãy số nào? Vì sao10không xuất hiện?- Khác biệt giữa
breakvàcontinuelà gì?elsecủa vòng lặp chạy khi nào? - Viết một list comprehension tạo danh sách bình phương các số lẻ từ 1 đến 20.
- Đoạn
whilesau bị lỗi gì và sửa thế nào:i = 0; while i < 3: print(i)?
Đọc tiếp
Bài viết liên quan
Vì sao Python là ngôn ngữ số một của data engineer: vai trò trong pipeline (ingest/transform/orchestrate), hệ sinh thái thư viện (pandas/polars/pyarrow/sqlalchemy), quản lý môi trường (venv/uv/poetry), và khi nào dùng Python vs SQL/Spark.
Định nghĩa hàm, tham số, *args/**kwargs, lambda, module/package, pip và virtualenv.
Lớp, kế thừa, đa hình, dunder methods, dataclass, type hints và nguyên tắc viết code sạch.
Vì sao Python, cài đặt, chạy script vs REPL, biến và kiểu dữ liệu cơ bản, quy ước code.