Hàm Lambda của Python: Cú pháp, cách sử dụng thực sự và khi nào không nên sử dụng chúng
Lambda là một hàm nhỏ mà bạn có thể viết thành một dòng, ngay tại nơi bạn cần. Tìm hiểu cú pháp, những nơi mà lambda thực sự trợ giúp, cái bẫy thu hút các nhà phát triển có kinh nghiệm và khi nào thì một chức năng bình thường sẽ tốt hơn.
- lambda là một hàm ẩn danh được giới hạn ở một biểu thức duy nhất.
- Cách sử dụng tốt nhất của nó là làm đối số ngắn, đặc biệt là
keychosorted,minvàmax. - Việc hiểu danh sách thường rõ ràng hơn
maphoặcfiltervới lambda. - Nếu bạn gán lambda cho một tên, thay vào đó hãy viết
def.
Bạn cần sắp xếp sản phẩm theo giá. Viết một hàm được đặt tên riêng chỉ để nói “sử dụng giá” có cảm giác như rất nhiều nghi lễ. Hàm lambda Python cho phép bạn xác định đoạn logic nội tuyến nhỏ đó, chính xác ở nơi bạn sử dụng.
Lambda rất tiện dụng nhưng chúng cũng dễ bị lạm dụng. Hướng dẫn này cho thấy chúng thực sự hữu ích ở đâu và làm cho mã khó đọc hơn ở đâu. Chúng tôi đã chạy mọi ví dụ trên Python 3.13 và kết quả hiển thị là từ lần chạy đó.
Lambda là gì?
Lambda là một hàm không có tên, được viết bằng từ khóa lambda:
lambda arguments: expression
Nó nhận bất kỳ số lượng đối số nào và tự động trả về giá trị của một biểu thức. Không có từ khóa return và không có nội dung câu lệnh.
square = lambda x: x * x
print(square(4)) # 16
print((lambda a, b=10: a + b)(5)) # 15
Dòng thứ hai xác định lambda với đối số mặc định và gọi nó ngay lập tức. Bạn hiếm khi viết mã này bằng mã thực, nhưng nó cho thấy lambda là một đối tượng hàm thông thường.
lambda vs def
Hai hàm này tạo ra các hàm hoạt động giống nhau:
square = lambda x: x * x
def square_def(x):
return x * x
print(square(4), square_def(4)) # 16 16
print(square.__name__, square_def.__name__) # <lambda> square_def
Nhìn vào những cái tên. Mọi lambda chỉ được gọi là <lambda>, vì vậy các thông báo lỗi và nhật ký không thể cho bạn biết cái nào bị lỗi. Đây là lý do chính khiến các hướng dẫn về kiểu như PEP 8 khuyên dùng def bất cứ khi nào bạn muốn đặt tên cho hàm.
| tính năng | lambda |
chắc chắn |
|---|---|---|
| Tên | Ẩn danh | Được đặt tên |
| Thân hình | Một biểu thức | Bất kỳ số lượng báo cáo |
| Trở lại | ngầm | Rõ ràng return |
| Gợi ý về chuỗi tài liệu và loại | Không được hỗ trợ | Được hỗ trợ |
| Truy nguyên có thể đọc được | Chương trình <lambda> |
Hiển thị tên |
| Tốt nhất cho | Những lập luận ngắn gọn | Mọi thứ khác |
Sắp xếp bằng khóa: cách sử dụng tốt nhất
Các hàm như sorted, min và max chấp nhận hàm key quyết định nội dung cần so sánh. Đây là nơi lambdas tỏa sáng:
products = [("pen", 10), ("bag", 900), ("book", 250)]
sorted(products, key=lambda p: p[1])
# [('pen', 10), ('book', 250), ('bag', 900)]
max(products, key=lambda p: p[1])
# ('bag', 900)
Để sắp xếp theo nhiều thứ, hãy trả về một bộ dữ liệu. Ở đây, mọi người được sắp xếp theo độ tuổi, già nhất trước tiên và sau đó theo tên:
people = [{"name": "Asha", "age": 31}, {"name": "Ben", "age": 25},
{"name": "Dev", "age": 31}]
[p["name"] for p in sorted(people, key=lambda p: (-p["age"], p["name"]))]
# ['Asha', 'Dev', 'Ben']
Dấu trừ chỉ đảo ngược thứ tự của số. Đôi khi bạn hoàn toàn không cần lambda vì một hàm hiện có đã thực hiện công việc đó. So sánh cách sắp xếp mặc định với cách sắp xếp không phân biệt chữ hoa chữ thường:
names = ["Asha", "ben", "Chitra", "dev"]
sorted(names) # ['Asha', 'Chitra', 'ben', 'dev']
sorted(names, key=str.lower) # ['Asha', 'ben', 'Chitra', 'dev']
Mặc định đặt tất cả các từ viết hoa đầu tiên. Vượt qua str.lower sẽ sửa lỗi đó mà không có bất kỳ lambda nào. Mô-đun operator cung cấp các trợ giúp tương tự, chẳng hạn như itemgetter(1) cho lambda p: p[1].
bản đồ và bộ lọc
map áp dụng một hàm cho mọi mục và filter giữ các mục mà nó trả về đúng. Cả hai đều hoạt động với lambdas, nhưng so sánh từng cái với cách hiểu danh sách:
nums = [1, 2, 3, 4, 5, 6]
list(map(lambda n: n * 10, nums)) # [10, 20, 30, 40, 50, 60]
[n * 10 for n in nums] # [10, 20, 30, 40, 50, 60]
list(filter(lambda n: n % 2 == 0, nums)) # [2, 4, 6]
[n for n in nums if n % 2 == 0] # [2, 4, 6]
Các kết quả giống hệt nhau và hầu hết các nhà phát triển Python đều thấy phần hiểu dễ đọc hơn. Cũng lưu ý rằng map và filter trả về các trình vòng lặp lười biếng, vì vậy bạn cần list() để xem các giá trị. Hướng dẫn của chúng tôi về hiểu danh sách Python trình bày chi tiết về phong cách đó. map vẫn đọc tốt với hàm được đặt tên hiện có, như trong map(str.strip, lines).
giảm bớt
functools.reduce kết hợp các mục thành một giá trị duy nhất, mang lại kết quả đang chạy:
from functools import reduce
reduce(lambda acc, n: acc + n, nums, 0) # 21
sum(nums) # 21
Đối với các công việc thông thường, chẳng hạn như tính tổng, hãy sử dụng các công cụ có sẵn như sum, max hoặc any. Giữ reduce cho các trường hợp hiếm hơn và xem xét vòng lặp đơn giản nếu lambda bắt đầu trông phức tạp.
Điều kiện trong lambda
Bạn không thể sử dụng câu lệnh if trong lambda, nhưng bạn có thể sử dụng biểu thức điều kiện:
label = lambda total: "large" if total >= 1000 else "small"
label(1200), label(300)
# ('large', 'small')
Một điều kiện là được. Một khi bạn bắt đầu lồng chúng, người đọc phải làm việc chăm chỉ, đó là một dấu hiệu rõ ràng để chuyển sang def với các câu lệnh if thông thường.
Lambda trả về các hàm
Một hàm có thể tạo và trả về một lambda ghi nhớ một giá trị từ môi trường xung quanh nó:
def multiplier(n):
return lambda x: x * n
double = multiplier(2)
double(7) # 14
Điều này hoạt động nhờ các bao đóng mà bài viết của chúng tôi về các bao đóng JavaScript giải thích bằng một ngôn ngữ khác. functools.partial của Python cung cấp một cách khác để điền trước các đối số.
Cái bẫy ràng buộc muộn
Điều này cuối cùng đã bắt được gần như tất cả mọi người. Chúng tôi đã tạo ba lambda trong một vòng lặp, hy vọng mỗi cái sẽ trả về số riêng của nó:
funcs = [lambda: i for i in range(3)]
[f() for f in funcs]
# [2, 2, 2]
Cả ba đều trả về 2. Một lambda không nắm bắt đượcgiá trịcủaikhi nó được tạo ra. Nó tra cứu biến khi nóđược gọi là, và lúc đó vòng lặp đã kết thúc, để lạiiở mức 2. Cách khắc phục thông thường là lấy giá trị làm đối số mặc định, vì các giá trị mặc định được đánh giá tại thời điểm xác định:
funcs = [lambda i=i: i for i in range(3)]
[f() for f in funcs]
# [0, 1, 2]
Điều này không phải là duy nhất đối với lambdas, vì def bên trong vòng lặp cũng hoạt động theo cách tương tự. Nó chỉ xuất hiện thường xuyên hơn với lambdas, vì chúng thường được tạo trong các vòng lặp, chẳng hạn như khi đính kèm các lệnh gọi lại nút.
Mẹo: Nếu bạn cần một chức năng cho mỗi mục, hãy cân nhắc xem bạn có cần nhiều chức năng hay không. Thông thường, một hàm duy nhất lấy mục làm đối số sẽ rõ ràng hơn và tránh hoàn toàn bẫy.
Bảng tra cứu các hàm nhỏ
Lambdas nằm gọn trong một từ điển ánh xạ tên cho các hoạt động nhỏ:
ops = {
"add": lambda a, b: a + b,
"sub": lambda a, b: a - b,
}
ops["sub"](10, 3) # 7
Điều này có thể thay thế một chuỗi dài if và elif. Hướng dẫn Từ điển Python bao gồm nhiều mẫu từ điển hơn và mô-đun operator đã cung cấp operator.add và operator.sub.
Giới hạn của lambdas
- Chỉ một biểu thức. Không có phép gán với
=, không có vòng lặp và không có khốitry. - Không có câu lệnh nào. Viết
lambda x: return xkhông thành công vớiSyntaxError, như thử nghiệm của chúng tôi đã xác nhận. - Không có gợi ý về chuỗi tài liệu hoặc loại. Các công cụ và người đọc nhận được ít trợ giúp hơn.
- Gỡ lỗi khó hơn. Lỗi chỉ vào
<lambda>thay vì tên hữu ích.
Tất nhiên, Lambdas vẫn có thể đưa ra các ngoại lệ. Việc xử lý những công việc đó cũng giống như đối với bất kỳ hàm nào, như được đề cập trong Xử lý ngoại lệ Python.
Khi nào không nên sử dụng lambda
| Tình huống | Lựa chọn tốt hơn |
|---|---|
| Gán nó cho một cái tên | chắc chắn |
| Logic cần nhiều hơn một dòng | chắc chắn |
| Chuyển đổi hoặc lọc danh sách | Hiểu danh sách |
| Tính tổng hoặc tìm giá trị lớn nhất | sum, max, min |
| Nhận một vật phẩm hoặc thuộc tính | operator.itemgetter hoặc attrgetter |
| Lambda tương tự xuất hiện ở một số nơi | Một hàm được đặt tên mà bạn có thể kiểm tra |
Đó là điểm cuối cùng quan trọng. Việc sử dụng lại logic đáng để thử nghiệm và hàm được đặt tên sẽ dễ kiểm tra hơn nhiều, như hướng dẫn pytest của chúng tôi cho thấy.
Lời kết
Một lambda tốt phải đủ ngắn để đọc nhanh và được sử dụng một lần, thường là đối số. Các phím sắp xếp là ví dụ cổ điển. Để kiểm tra mã của riêng bạn, hãy tìm kiếm = lambda và biến chúng thành các hàm được đặt tên. Sau đó, nhìn vào bất kỳ map hoặc filter nào bằng lambda và xem liệu khả năng hiểu có đọc tốt hơn hay không.
Câu hỏi thường gặp
Hàm lambda trong Python là gì?
Đó là một hàm ẩn danh nhỏ được tạo bằng từ khóa lambda. Nó nhận các đối số và trả về giá trị của một biểu thức đơn lẻ, không có tên hoặc câu lệnh trả về.
Lambda Python có thể có nhiều dòng không?
Không. Lambda được giới hạn ở một biểu thức. Bạn có thể chia nó thành các dòng bên trong dấu ngoặc, nhưng nó vẫn không thể chứa các câu lệnh, vì vậy hãy sử dụng def cho logic phức tạp hơn.
Tôi có thể sử dụng if else trong lambda không?
Bạn có thể sử dụng biểu thức điều kiện, chẳng hạn như lambda x: “big” nếu x > 10 nếu không thì “nhỏ”. Câu lệnh if có khối không được phép.
Lambda có nhanh hơn các chức năng bình thường không?
Không có ý nghĩa. Cả hai đều tạo ra cùng một loại đối tượng hàm. Chọn giữa chúng để dễ đọc chứ không phải hiệu suất.
Tôi có nên sử dụng bản đồ với lambda hoặc hiểu danh sách không?
Hầu hết các nhà phát triển Python thích khả năng hiểu danh sách để dễ đọc. bản đồ là hợp lý khi bạn đã có một hàm được đặt tên, chẳng hạn như bản đồ (str.strip, dòng).
Tiếp tục học hỏi
Cần trợ giúp về kiểm tra, WordPress hoặc SEO?
Chúng tôi giúp các nhóm phát triển phần mềm tốt hơn và phát triển trang web của họ. Hãy cho chúng tôi biết bạn đang làm gì và chúng tôi sẽ đề xuất bước tiếp theo rõ ràng.
Thêm hướng dẫn về thử nghiệm, AI và phát triển hiện đại
Bạn thích bài viết này? Chọn một chủ đề và chuyển ngay sang phần hướng dẫn mới nhất của chúng tôi.
Giải thích về ảo giác AI: Tại sao các mô hình ngôn ngữ lại tạo nên mọi thứ và cách giảm thiểu nó
Ảo giác AI đã giải thích: chúng là gì, tại sao mô hình ngôn ngữ tạo ra chúng, các loại phổ biến, cách giảm thiểu chúng bằng cách căn cứ và nhắc nhở …
Đọc bài viết →DevOpsGiải thích về các biến môi trường: Tệp .env, bí mật và cấu hình được thực hiện đúng
Các biến môi trường được giải thích bằng các ví dụ Python mà chúng tôi đã chạy: đọc và thiết lập chúng, tại sao mọi giá trị đều là một chuỗi, tệp .e…
Đọc bài viết →Phát triển phần mềmGiải thích về giới hạn tỷ lệ API: Thuật toán, tiêu đề và xử lý lỗi 429
Giới hạn tốc độ API được giải thích bằng mô phỏng mà chúng tôi đã chạy: nhóm mã thông báo, cửa sổ cố định, cửa sổ trượt, 429 phản hồi, tiêu đề giới …
Đọc bài viết →Tự động hóa kiểm thửChế giễu bằng Python: unittest.mock, patch và những sai lầm khiến bài kiểm tra nói dối
Mô phỏng bằng Python với unittest.mock, đã được thử nghiệm: Mock và return_value, side_effect, vá lỗi nơi sử dụng tên, thông số kỹ thuật và thông số…
Đọc bài viết →Kiểm thử phần mềmGiải thích về phạm vi bảo hiểm của mã: Phạm vi tuyến so với chi nhánh và ý nghĩa thực sự 100%
Phạm vi bảo hiểm mã được giải thích bằng cách chạy phạm vi bảo hiểm thực tế: phạm vi bảo hiểm dòng, chi nhánh và điều kiện, đọc báo cáo, lý do tại s…
Đọc bài viết →Cơ sở dữ liệu & SQLDELETE vs TRUNCATE vs DROP trong SQL: Sự khác biệt, khôi phục và thời điểm sử dụng mỗi loại
DELETE vs TRUNCATE vs DROP được giải thích bằng SQL đã được thử nghiệm: mỗi loại loại bỏ, khôi phục, kích hoạt, đặt lại danh tính, tốc độ và Postgre…
Đọc bài viết →Cơ sở dữ liệu & SQLGiải thích về Chế độ xem SQL: Tạo, sử dụng và tránh những cạm bẫy, kèm theo ví dụ
Chế độ xem SQL được giải thích bằng các truy vấn chúng tôi đã chạy: tạo chế độ xem, kết quả trực tiếp, ẩn cột, hiệu suất, chế độ xem có thể cập nhật…
Đọc bài viết →Phát triển phần mềmGiao diện TypeScript so với loại: Sự khác biệt thực sự, được thử nghiệm bằng trình biên dịch
Giao diện TypeScript so với loại, được kiểm tra bằng trình biên dịch thực: cả hai có thể làm gì, chỉ một người có thể làm gì, hợp nhất khai báo, mở …
Đọc bài viết →Phát triển phần mềmGiải thích về Python *args và **kwargs: Hàm linh hoạt với ví dụ rõ ràng
Python *args và **kwargs được giải thích bằng mã chúng tôi đã chạy: đóng gói và giải nén, thứ tự tham số, đối số chỉ từ khóa và chỉ vị trí, trình tr…
Đọc bài viết →Phát triển phần mềmHàm Lambda của Python: Cú pháp, cách sử dụng thực tế và khi nào không nên sử dụng chúng
Các hàm lambda của Python được giải thích bằng mã chúng tôi đã chạy: cú pháp, sắp xếp bằng khóa, bản đồ và bộ lọc, các phần đóng, bẫy liên kết muộn …
Đọc bài viết →Trí tuệ nhân tạoĐại lý AI là gì? Cách chúng hoạt động, với vòng lặp tác nhân đơn giản, bạn có thể chạy
Tác nhân AI là gì? Hướng dẫn đơn giản về vòng lặp tác nhân, công cụ, bộ nhớ và lan can, với một vòng lặp Python nhỏ mà chúng tôi đã chạy, những rủi …
Đọc bài viết →DevOpsGiám sát và Khả năng quan sát: Sự khác biệt thực sự, với nhật ký, số liệu và dấu vết
Giải thích về giám sát và khả năng quan sát: lỗi đã biết và chưa biết, nhật ký, số liệu và dấu vết, hướng dẫn về sự cố thanh toán, tín hiệu vàng, Op…
Đọc bài viết →DevOpsDocker Volumes vs Bind Mounts: Duy trì dữ liệu vùng chứa đúng cách
Giải thích về khối lượng Docker và liên kết gắn kết: tại sao dữ liệu vùng chứa biến mất, khối lượng được đặt tên, liên kết gắn kết, tmpfs, ví dụ về …
Đọc bài viết →Tự động hóa kiểm thửTDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each
TDD và BDD được giải thích bằng một tính năng giảm giá được xây dựng theo cả hai cách: tái cấu trúc màu đỏ-xanh với mã chúng tôi đã chạy, kịch bản C…
Đọc bài viết →Phát triển phần mềmGit merge vs rebase: khác biệt, ví dụ và khi nào dùng cái nào
Git merge vs rebase được giải thích bằng đầu ra log thực tế, bảng so sánh, quy tắc vàng của rebase và quy trình làm việc nhóm an toàn.
Đọc bài viết →Phát triển phần mềmList comprehension trong Python: cú pháp, ví dụ và khi nào nên tránh
List comprehension trong Python: cú pháp, lọc dữ liệu, if-else, vòng lặp lồng nhau, thời gian đo thực tế và khi nào vòng lặp for thông thường tốt hơn.
Đọc bài viết →Cơ sở dữ liệu & SQLGiải thích SQL JOIN kèm ví dụ (Inner, Left, Right, Full)
Hiểu SQL JOIN từng bước với bảng và truy vấn mẫu: INNER, LEFT, RIGHT, FULL, CROSS và SELF, cùng lỗi thường gặp và mẹo hiệu năng.
Đọc bài viết →