DHDataHọc
TheoryLesson 18/304 min read+10 XP

List comprehension

Bạn sẽ học được

  • Cú pháp comprehension và cách đọc nó thành câu.
  • Lọc bằng if, chọn giá trị bằng if ... else.
  • Dict comprehension và set comprehension.
  • Duyệt nhiều vòng lặp, làm phẳng ma trận.
  • Generator expression và giới hạn đọc hiểu.

Khái niệm cốt lõi

Comprehension tạo collection mới từ iterable trong một biểu thức. Dạng cơ bản:

code
squares = [x * x for x in range(10) if x % 2 == 0]
print(squares)
code
[0, 4, 16, 36, 64]

Đọc thành câu: "lấy x * x với mỗi x trong range(10), nếu x chẵn". Ba phần:

PhầnTrong ví dụVai trò
Biểu thứcx * xGiá trị đưa vào list
Vòng lặpfor x in range(10)Nguồn dữ liệu
Điều kiệnif x % 2 == 0Lọc (tuỳ chọn)

Tương đương vòng lặp thường:

code
squares = []
for x in range(10):
    if x % 2 == 0:
        squares.append(x * x)

Comprehension ngắn hơn, nhưng chỉ nên dùng khi biểu thức còn đọc được trong một dòng. Khi có nhiều điều kiện lồng nhau, vòng lặp thường rõ hơn.

if ... else trong biểu thức

Có hai vị trí if khác nhau, rất dễ nhầm:

code
labels = ["đạt" if s >= 50 else "trượt" for s in [45, 60, 50]]
print(labels)
code
['trượt', 'đạt', 'đạt']

if ... else đứng trước for là biểu thức điều kiện — mỗi phần tử luôn được tạo, giá trị phụ thuộc điều kiện. Còn if đứng sau for là bộ lọc — một số phần tử bị bỏ hẳn:

code
passed = [s for s in [45, 60, 50] if s >= 50]
print(passed)
code
[60, 50]

Nếu viết [s if s >= 50 for s in ...] sẽ là SyntaxError: biểu thức ba ngôi cần đủ else. Quy tắc: if sau for thì lọc; if ... else trước for thì chọn giá trị.

Dict và set comprehension

Cùng cú pháp, đổi ngoặc:

code
rows = [("An", 85), ("Bình", 92)]
print({name: score for name, score in rows})
code
{'An': 85, 'Bình': 92}
code
print(sorted({len(name) for name in ["An", "Bình", "Chi"]}))
code
[2, 3, 4]

Dict comprehension cần cặp key: value; khóa trùng sẽ ghi đè, giữ vị trí lần đầu. Set comprehension tự khử trùng, nên đừng dùng nó khi cần đếm số lần.

Nhiều vòng lặp

Có thể lồng nhiều for, đọc từ trái sang phải như vòng ngoài đến vòng trong:

code
matrix = [[1, 2], [3, 4]]
print([n for row in matrix for n in row])
code
[1, 2, 3, 4]

Thêm điều kiện vẫn đặt sau tất cả các for:

code
matrix = [[1, 2], [3, 4]]
print([n * 2 for row in matrix for n in row if n > 2])
code
[6, 8]

Hai for thường vẫn đọc được. Ba for trở lên gần như luôn nên viết thành vòng lặp thường — người đọc phải tự bóc từng lớp.

Generator expression

Đổi [] thành () được generator — không tạo list, sinh từng giá trị khi cần:

code
print(sum(x * x for x in range(1000)))
code
332833500

Generator tiết kiệm bộ nhớ vì không giữ toàn bộ dãy. Khi dùng với sum, max, min, any, all, không cần ngoặc vuông:

code
numbers = [4, 7, 10, 13]
print(any(n % 2 == 1 for n in numbers))
code
True

Lưu ý generator chỉ duyệt được một lần. Nếu cần dùng lại, tạo list hoặc tạo generator mới.

Hiệu năng và đọc hiểu

Comprehension thường nhanh hơn vòng lặp + append vì chạy trong C, nhưng khác biệt chỉ đáng kể với dữ liệu lớn. Quan trọng hơn là độ rõ:

Tình huốngNên dùng
Biến đổi đơn giản, một điều kiệnComprehension
Có tác dụng phụ (in, ghi file)Vòng lặp thường
Ba vòng lặp trở lênVòng lặp thường
Cần try/except trong thânVòng lặp thường
Chỉ tiêu thụ một lần, dữ liệu lớnGenerator expression

Comprehension không nên chứa tác dụng phụ. [print(x) for x in data] chạy được nhưng tạo list toàn None và bị lint cảnh báo — dùng vòng lặp.

Thực hành

  1. Tạo list bình phương các số chẵn từ 0 đến 9.
  2. Tạo nhãn đạt/trượt bằng if ... else trước for.
  3. Lọc danh sách điểm từ 50 trở lên bằng if sau for.
  4. Đổi list (tên, điểm) thành dict.
  5. Làm phẳng ma trận [[1, 2], [3, 4]].
  6. Tính tổng bình phương 1000 số đầu bằng generator.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
SyntaxError ở if giữaThiếu else trong biểu thức ba ngôiThêm else hoặc chuyển if ra sau for
Kết quả khác mong đợiNhầm lọc với chọn giá trịif sau for lọc; if ... else trước for chọn
Dict comprehension lỗiThiếu key: valueViết {k: v for ...}
Generator dùng lần hai ra rỗngGenerator chỉ duyệt một lầnTạo list hoặc generator mới
Comprehension quá dàiNhiều for/if lồngChuyển về vòng lặp thường
List toàn NoneDùng comprehension với printDùng vòng lặp cho tác dụng phụ
NameError trong biểu thứcBiến chỉ có trong vòng lặpKiểm tra phạm vi biến

Tóm tắt

  • Comprehension gồm biểu thức + for + điều kiện tuỳ chọn.
  • if sau for để lọc; if ... else trước for để chọn giá trị.
  • {} cho dict/set, () cho generator.
  • Nhiều for đọc từ ngoài vào trong; quá hai lớp nên dùng vòng lặp.
  • Generator tiết kiệm bộ nhớ nhưng chỉ dùng một lần.

Use ← → to move between lessons