Bài 3.22. List comprehension - cú pháp đặc trưng của Python

Chào mừng bạn đến với một trong những tính năng thanh lịchmạnh mẽ nhất của Python: List Comprehension. Đây là một cách cực kỳ Pythonic (mang đậm phong cách Python) để tạo ra các danh sách (list) một cách ngắn gọn và dễ đọc hơn so với việc sử dụng các vòng lặp for truyền thống kết hợp với phương thức .append().

Nếu bạn muốn viết code Python hiệu quả và dễ hiểu, nắm vững list comprehension là một kỹ năng không thể thiếu. Hãy cùng đi sâu vào cú pháp và các ứng dụng tuyệt vời của nó!

Cú Pháp Cơ Bản

Cú pháp đơn giản nhất của list comprehension có dạng:

new_list = [expression for item in iterable]

Hãy phân tích từng thành phần:

  • expression: Biểu thức được áp dụng cho mỗi item. Kết quả của biểu thức này sẽ là phần tử trong danh sách mới.
  • item: Biến đại diện cho từng phần tử trong iterable.
  • iterable: Bất kỳ đối tượng nào có thể lặp qua (ví dụ: list, tuple, string, range,...).

Ví dụ 1: Tạo danh sách các số bình phương

Giả sử bạn muốn tạo một danh sách chứa bình phương của các số từ 0 đến 9.

Cách truyền thống dùng for loop:

squares = [] # Khởi tạo danh sách rỗng
for x in range(10): # Lặp qua các số từ 0 đến 9
    squares.append(x**2) # Tính bình phương và thêm vào danh sách

print(squares)
# Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Cách dùng List Comprehension:

squares_comp = [x**2 for x in range(10)]

print(squares_comp)
# Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Giải thích:

  • x**2: Đây là expression. Với mỗi x, chúng ta tính bình phương của nó.
  • for x: Đây là item. x lần lượt nhận các giá trị từ iterable.
  • in range(10): Đây là iterable, cung cấp các số từ 0 đến 9.

Bạn thấy đấy, list comprehension giúp viết code ngắn gọndễ đọc hơn rất nhiều!

Thêm Điều Kiện Lọc (Filtering)

List comprehension còn cho phép bạn thêm một điều kiện if để lọc các phần tử từ iterable trước khi áp dụng expression.

Cú pháp:

new_list = [expression for item in iterable if condition]
  • condition: Biểu thức điều kiện. Chỉ những item nào làm cho condition trả về True mới được xử lý bởi expression.

Ví dụ 2: Lấy các số chẵn từ một danh sách

Cách truyền thống dùng for loop và if:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = []
for num in numbers:
    if num % 2 == 0: # Kiểm tra xem số có chẵn không
        even_numbers.append(num)

print(even_numbers)
# Output: [2, 4, 6, 8, 10]

Cách dùng List Comprehension với if:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers_comp = [num for num in numbers if num % 2 == 0]

print(even_numbers_comp)
# Output: [2, 4, 6, 8, 10]

Giải thích:

  • num: expression ở đây chỉ đơn giản là lấy chính phần tử num.
  • for num in numbers: Lặp qua từng num trong numbers.
  • if num % 2 == 0: condition. Chỉ những num nào chia hết cho 2 (là số chẵn) mới được đưa vào danh sách mới.

Ví dụ 3: Lấy các từ có độ dài lớn hơn 5

words = ["apple", "banana", "kiwi", "strawberry", "orange", "grape"]
long_words = [word.upper() for word in words if len(word) > 5] # Đồng thời chuyển thành chữ hoa

print(long_words)
# Output: ['BANANA', 'STRAWBERRY', 'ORANGE']

Giải thích:

  • word.upper(): expression. Lấy từ word và chuyển thành chữ hoa.
  • for word in words: Lặp qua từng word trong danh sách words.
  • if len(word) > 5: condition. Chỉ những word có độ dài lớn hơn 5 mới được chọn và xử lý bởi expression.

Sử Dụng Biểu Thức Điều Kiện (Conditional Expression) trong expression

Đôi khi, bạn muốn áp dụng các expression khác nhau dựa trên một điều kiện cho mỗi item. Bạn có thể sử dụng biểu thức điều kiện (còn gọi là toán tử ba ngôi - ternary operator) ngay trong phần expression.

Cú pháp của biểu thức điều kiện: value_if_true if condition else value_if_false

Kết hợp với list comprehension:

new_list = [value_if_true if condition else value_if_false for item in iterable]

Quan trọng: Đừng nhầm lẫn điều này với mệnh đề if lọc ở phần trước. Mệnh đề if lọc đặt ở cuối và quyết định xem item có được xử lý hay không. Biểu thức điều kiện if...else đặt ở đầu (trong expression) và quyết định kết quả của expression cho mỗi item.

Ví dụ 4: Phân loại số chẵn/lẻ

numbers = range(10) # Các số từ 0 đến 9
parity = ["Even" if x % 2 == 0 else "Odd" for x in numbers]

print(parity)
# Output: ['Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd']

Giải thích:

  • "Even" if x % 2 == 0 else "Odd": Đây là expression sử dụng biểu thức điều kiện. Nếu x chia hết cho 2, kết quả là chuỗi "Even", ngược lại là "Odd".
  • for x in numbers: Lặp qua từng x trong range(10).

So sánh với cách dùng for loop:

numbers = range(10)
parity_loop = []
for x in numbers:
    if x % 2 == 0:
        parity_loop.append("Even")
    else:
        parity_loop.append("Odd")
print(parity_loop)
# Output: ['Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd']

List comprehension một lần nữa thể hiện sự ngắn gọn vượt trội.

List Comprehension Lồng Nhau (Nested List Comprehensions)

Bạn có thể sử dụng list comprehension để thay thế cho các vòng lặp for lồng nhau. Điều này đặc biệt hữu ích khi làm việc với các cấu trúc dữ liệu đa chiều như ma trận (danh sách của các danh sách).

Cú pháp:

new_list = [expression for outer_item in outer_iterable for inner_item in inner_iterable]

Thứ tự for rất quan trọng: Thứ tự các mệnh đề for trong list comprehension lồng nhau tương ứng với thứ tự các vòng lặp for từ ngoài vào trong.

Ví dụ 5: Làm phẳng (flatten) một danh sách 2 chiều

Giả sử bạn có một ma trận và muốn tạo một danh sách chứa tất cả các phần tử của nó.

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

Cách dùng vòng lặp for lồng nhau:

flat_list_loop = []
for row in matrix: # Vòng lặp ngoài
    for num in row: # Vòng lặp trong
        flat_list_loop.append(num)

print(flat_list_loop)
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Cách dùng Nested List Comprehension:

flat_list_comp = [num for row in matrix for num in row]

print(flat_list_comp)
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Giải thích:

  • num: expression. Chúng ta chỉ đơn giản lấy phần tử num.
  • for row in matrix: Vòng lặp ngoài, lặp qua từng row (danh sách con) trong matrix.
  • for num in row: Vòng lặp trong, lặp qua từng num trong row hiện tại.

Ví dụ 6: Tạo một ma trận chuyển vị

matrix = [
    [1, 2, 3],
    [4, 5, 6]
]

# Kích thước: 2 hàng, 3 cột
# Muốn tạo ma trận chuyển vị: 3 hàng, 2 cột

# Dùng list comprehension lồng nhau
transpose_comp = [[row[i] for row in matrix] for i in range(len(matrix[0]))]

print(transpose_comp)
# Output: [[1, 4], [2, 5], [3, 6]]

Giải thích:

  • Vòng lặp ngoài for i in range(len(matrix[0])): Lặp qua các chỉ số cột của ma trận gốc (0, 1, 2). Mỗi giá trị i sẽ tạo ra một hàng mới trong ma trận chuyển vị.
  • Vòng lặp trong (comprehension bên trong) [row[i] for row in matrix]: Đối với mỗi chỉ số cột i, nó lặp qua tất cả các row của ma trận gốc và lấy phần tử tại cột i (row[i]). Kết quả là một danh sách chứa các phần tử của cột i trong ma trận gốc, đây chính là hàng i của ma trận chuyển vị.

Lưu ý về Nested List Comprehensions: Mặc dù mạnh mẽ, list comprehension lồng nhau quá phức tạp có thể trở nên khó đọc. Nếu bạn có nhiều hơn hai vòng lặp lồng nhau hoặc logic phức tạp, việc sử dụng các vòng lặp for truyền thống có thể giúp code rõ ràng hơn. Hãy cân nhắc giữa sự ngắn gọn và tính dễ hiểu.

Ưu điểm và Khi nào nên sử dụng

  • Ngắn gọn và Súc tích: Giảm đáng kể số dòng code so với vòng lặp for.append().
  • Dễ đọc (thường là vậy): Đối với các tác vụ tạo danh sách đơn giản và phổ biến, list comprehension thường dễ đọc và hiểu ý định hơn.
  • Hiệu suất: Trong nhiều trường hợp, list comprehension có thể nhanh hơn một chút so với việc sử dụng for loop và .append() do một số tối ưu hóa nội bộ của Python.

Nên sử dụng list comprehension khi:

  • Bạn muốn tạo một danh sách mới từ một iterable có sẵn.
  • Logic để tạo từng phần tử là tương đối đơn giản.
  • Bạn cần lọc các phần tử từ iterable dựa trên một điều kiện.
  • Bạn muốn áp dụng một biểu thức đơn giản cho mỗi phần tử được chọn.

Khi nào không nên (hoặc cân nhắc kỹ):

  • Khi logic quá phức tạp, làm cho list comprehension trở nên dài dòng và khó đọc.
  • Khi bạn cần thực hiện các tác vụ có tác dụng phụ (side effects) bên trong vòng lặp (ví dụ: in ra màn hình, sửa đổi đối tượng khác ngoài danh sách đang tạo). List comprehension chủ yếu dùng để tạo giá trị, không phải để thực hiện hành động.
  • Khi bạn làm việc với các cấu trúc lồng nhau quá sâu (nested loops).

List comprehension là một công cụ tuyệt vời trong bộ công cụ của lập trình viên Python. Bằng cách luyện tập và sử dụng chúng một cách hợp lý, bạn có thể viết code Python hiệu quả, ngắn gọndễ đọc hơn đáng kể.

Comments

There are no comments at the moment.