Học bằng cách đọc theo thứ tự

map() — Áp dụng một hàm cho mọi phần tử của list cùng lúc

Học hàm map() trong Python để áp dụng cùng một hàm cho mọi phần tử của list, kết hợp tự nhiên với lambda.

Trong bài trước về decorator, bạn đã thấy cách bọc hành vi bổ sung quanh một hàm. Lần này, bạn sẽ chuyển hướng và xem hàm built-in map() — công cụ bạn với tới khi muốn áp dụng cùng một hàm cho mọi phần tử của list cùng lúc.

map() kết hợp tự nhiên với lambda, và cơ chế bên dưới tương tự list comprehension. Ngày nay, comprehension thường là lựa chọn được khuyên dùng hơn map().

map() là gì — Hàm bậc cao đụng tới mọi phần tử

map(function, iterable) áp dụng hàm của tham số đầu tiên cho mọi phần tử của tham số thứ hai (một list, tuple, v.v.) và trả về một đối tượng map cho phép bạn lấy ra kết quả từng cái một. Bạn có thể lặp nó với for, hoặc bọc trong list() để vật chất hóa toàn bộ thành list.

Vì tham số đầu tiên của nó là một hàm, chính map() được tính là hàm bậc cao.

Cách map() hoạt động
[1, 2, 3, 4, 5]lambda x: x ** 2<map object>(iterator)[1, 4, 9, 16, 25]đưa từng phần tửmap()lấy ra bằng list()
Mỗi phần tử của tham số thứ hai được chuyển qua hàm của tham số đầu, và kết quả quay về dưới dạng đối tượng map. Bọc trong list() để vật chất hóa nội dung.
numbers = [1, 2, 3, 4, 5]

squared = map(lambda x: x ** 2, numbers)
print(type(squared))   # <class 'map'>
print(squared)         # <map object at 0x...>

# Lấy giá trị bằng list()
print(list(squared))   # [1, 4, 9, 16, 25]

# Hàm built-in cũng dùng được (chuyển list chuỗi thành int)
str_nums = ["10", "20", "30"]
print(list(map(int, str_nums)))   # [10, 20, 30]

Đối tượng map dùng một lần

Giá trị trả về của map() là một iterator — cùng loại bạn nhận từ generator — và một khi bạn đã đi qua, nó rỗng. Gọi list(squared) lần thứ hai và bạn sẽ nhận []. Nếu cần dùng kết quả nhiều hơn một lần, làm list(map(...)) trước và làm việc với list.

Xây list giá đã thuế (10%) từ list giá sản phẩm prices.

① Khai báo prices = [100, 250, 480, 1200].

② Nhân mỗi phần tử với 1.1, chuyển thành int, xây list kết quả, và gán nó vào tax_included.

③ In kết quả với print(tax_included).

(Khi đáp án đúng, phần giải thích sẽ xuất hiện.)

Python Editor

Chạy code để xem đầu ra

Truyền nhiều iterable cùng lúc

Từ tham số thứ hai trở đi, map() chấp nhận nhiều iterable. Trong trường hợp đó, hàm của tham số đầu cần chấp nhận một phần tử từ mỗi iterable ở vị trí khớp. Viết map(function, A, B) và hàm được áp dụng cho cặp A[0]B[0], rồi A[1]B[1], v.v.

Ví dụ, để nhân hai list số theo phần tử, map(lambda a, b: a * b, A, B) cho bạn list các tích từng cặp.

A = [1, 2, 3]
B = [10, 20, 30]

# Nhân các phần tử cùng index
print(list(map(lambda a, b: a * b, A, B)))   # [10, 40, 90]

# Tương tự với ba iterable
def calculate(x, y, op):
    return x + y if op == "plus" else x - y

xs = [10, 20, 30]
ys = [3,  3,  3]
ops = ["plus", "minus", "plus"]

print(list(map(calculate, xs, ys, ops)))     # [13, 17, 33]
Truyền hai list cho map()
lambda a, b: a * bA = [1, 2, 3]B = [10, 20, 30](1, 10)(2, 20)(3, 30)104090đầu tiên1*102*203*30
Các phần tử của AB ở cùng index được ghép cặp và đưa vào lambda lambda a, b: a * b.

Độ dài lệch nhau dừng ở cái ngắn nhất

Khi các iterable bạn truyền có độ dài khác nhau, map() dừng ở cái ngắn nhất. Ví dụ, map(f, [1, 2, 3, 4], [10, 20]) chỉ chạy hai lần. Nếu muốn căn chỉnh rõ ràng, kiểm tra độ dài trước, hoặc kết hợp map() với `zip()` tùy nhu cầu.

Từ một list tên sản phẩm và một list số lượng, xây list chuỗi "tên × số lượng" dùng map().

① Khai báo items = ["táo", "cam", "nho"]counts = [3, 5, 2].

② Bọc map(lambda name, n: f"{name} × {n}", items, counts) trong list(...) và gán vào labels.

③ In từng cái trên dòng riêng với for label in labels: print(label).

Python Editor

Chạy code để xem đầu ra

map() vs List Comprehension — Bạn nên dùng cái nào?

Hầu như mọi thứ map() làm được, list comprehension cũng làm được. Tài liệu chính thức của Python và sách như Fluent Python khuyên ưu tiên comprehension trong code mới, và trong Python hiện đại, comprehension là dạng chuẩn.

map() từng là mặc định trong code cũ, nhưng vai trò của nó đã thu hẹp lại còn các trường hợp bạn có thể trao một tên hàm có sẵn mà không cần viết lambda (nghĩ map(int, str_nums)). Hãy xếp các khác biệt cạnh nhau.

Mục tiêuVới map()Với comprehension
Bình phương mọi phần tửlist(map(lambda x: x ** 2, nums))[x ** 2 for x in nums]
Chuyển str thành intlist(map(int, str_nums))[int(s) for s in str_nums]
Hai list cùng lúclist(map(f, A, B))[f(a, b) for a, b in zip(A, B)]
nums = [1, 2, 3, 4, 5]

# Phiên bản map()
print(list(map(lambda x: x ** 2, nums)))   # [1, 4, 9, 16, 25]

# Phiên bản comprehension (được ưu tiên)
print([x ** 2 for x in nums])              # [1, 4, 9, 16, 25]

# Khi truyền tên hàm trực tiếp được, map() trông sạch hơn
str_nums = ["10", "20", "30"]
print(list(map(int, str_nums)))            # [10, 20, 30]
print([int(s) for s in str_nums])          # [10, 20, 30]

# Khi lọc, comprehension cô đọng hơn nhiều
print([x for x in nums if x % 2 == 0])     # [2, 4]

Khi do dự, với tay tới comprehension

Dù bạn muốn biến đổi, lọc, hay làm cả hai, comprehension cho phép bạn viết tất cả tự nhiên trong cùng cặp ngoặc. Nếu bạn cam kết dùng map() chỉ khi trao một tên hàm có sẵn nguyên dạng, code của bạn vẫn nhất quán. Nó nhẹ hơn cho cả người viết và người đọc, nên ưu tiên comprehension trong code mới.

Chuyển một list chuỗi đầu vào thành list số nguyên theo hai cách: với map() và với comprehension.

① Khai báo inputs = ["10", "20", "30", "40"].

② Bọc map(int, inputs) trong list(...), gán vào via_map, và print(via_map).

③ Xây cùng kết quả với [int(s) for s in inputs], gán vào via_comp, và print(via_comp).

④ Cuối cùng, chạy print(via_map == via_comp) để xác nhận hai giá trị bằng nhau.

Python Editor

Chạy code để xem đầu ra
QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Code này in gì?
nums = [1, 2, 3]
print(list(map(lambda x: x + 10, nums)))

Câu 2Comprehension nào tạo cùng kết quả với list(map(int, ["1", "2", "3"]))?

Câu 3Phát biểu nào về giá trị trả về của map()đúng?