Tại sao Python lambdas hữu ích? [đóng cửa]


929

Tôi đang cố gắng tìm ra Python lambdas. Có phải lambda là một trong những mục ngôn ngữ "thú vị" mà trong đời thực nên bị lãng quên?

Tôi chắc chắn có một số trường hợp cạnh có thể cần thiết, nhưng với sự tối nghĩa của nó, tiềm năng của nó sẽ được xác định lại trong các bản phát hành trong tương lai (giả định của tôi dựa trên các định nghĩa khác nhau của nó) và độ rõ của mã hóa giảm - nên được tránh?

Điều này nhắc nhở tôi về việc tràn (tràn bộ đệm) của các loại C - chỉ vào biến trên cùng và quá tải để đặt các giá trị trường khác. Nó cảm thấy giống như một màn trình diễn công nghệ nhưng cơn ác mộng bảo trì.


261
+1 Câu hỏi hay - giả định xấu (che khuất lambda) =) Cố gắng không phán xét các kỹ thuật lập trình. Đánh giá chúng, và thêm chúng vào bộ công cụ tinh thần của bạn. Nếu bạn không thích chúng, đừng sử dụng chúng, và sẵn sàng thảo luận về chúng một cách logic mà không trở nên tôn giáo.
Kieveli

41
Quy tắc Haskell! Các chức năng Lambda cung cấp cho bạn sức mạnh biểu cảm và trừu tượng.
Jonathan Barbero

11
@JAL Không đề cập đến LISP ...
Cách tiếp

8
@ApproachingDarknessFish "Ah, đó là Cha mẹ của bạn. Một vũ khí văn minh hơn từ thời đại văn minh hơn." - Obi Lisp Kenobi
Các trường

Câu trả lời:


1009

Bạn đang nói về chức năng lambda ? Giống

lambda x: x**2 + 2*x - 5

Những thứ đó thực sự khá hữu ích. Python hỗ trợ một kiểu lập trình gọi là lập trình chức năng nơi bạn có thể truyền các hàm cho các hàm khác để thực hiện công cụ. Thí dụ:

mult3 = filter(lambda x: x % 3 == 0, [1, 2, 3, 4, 5, 6, 7, 8, 9])

đặt mult3thành [3, 6, 9], các yếu tố của danh sách gốc là bội số của 3. Điều này ngắn hơn (và, người ta có thể tranh luận, rõ ràng hơn) so với

def filterfunc(x):
    return x % 3 == 0
mult3 = filter(filterfunc, [1, 2, 3, 4, 5, 6, 7, 8, 9])

Tất nhiên, trong trường hợp cụ thể này, bạn có thể làm điều tương tự như việc hiểu danh sách:

mult3 = [x for x in [1, 2, 3, 4, 5, 6, 7, 8, 9] if x % 3 == 0]

(hoặc thậm chí là range(3,10,3)), nhưng có nhiều trường hợp sử dụng phức tạp hơn, phức tạp hơn trong đó bạn không thể sử dụng chức năng hiểu danh sách và chức năng lambda có thể là cách ngắn nhất để viết ra một cái gì đó.

  • Trả lại một chức năng từ một chức năng khác

    >>> def transform(n):
    ...     return lambda x: x + n
    ...
    >>> f = transform(3)
    >>> f(4)
    7
    

    Điều này thường được sử dụng để tạo các hàm bao hàm, chẳng hạn như trang trí của Python.

  • Kết hợp các yếu tố của một chuỗi lặp với reduce()

    >>> reduce(lambda a, b: '{}, {}'.format(a, b), [1, 2, 3, 4, 5, 6, 7, 8, 9])
    '1, 2, 3, 4, 5, 6, 7, 8, 9'
    
  • Sắp xếp theo khóa thay thế

    >>> sorted([1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: abs(5-x))
    [5, 4, 6, 3, 7, 2, 8, 1, 9]
    

Tôi sử dụng các chức năng lambda một cách thường xuyên. Phải mất một thời gian để tôi quen với chúng, nhưng cuối cùng tôi cũng hiểu rằng chúng là một phần rất có giá trị của ngôn ngữ.


26
Yêu các ví dụ, rất dễ hiểu. Nhưng đối với phần giảm. Nếu tôi phải thực hiện tính năng này. Tôi sẽ làm','.join(str(x) for x in [1,2,3,4,5,6,7,8,9])
etlds

3
BTW nếu bạn chạy cái này trên Python3, bạn cần gọi danh sách trên các kết quả của bộ lọc để xem các giá trị thực tế, ngoài ra bạn cần nhập giảm từ funcools
Gerard

Chúng tôi có chắc chắn về định nghĩa của "lập trình chức năng" ở trên không? Nó đến một chút khó hiểu với tôi.
stdout

3
Tôi nghĩ điểm quan trọng là các lambdachức năng có thể ẩn danh (vì chúng có trong tất cả các ví dụ của bạn). Nếu bạn đang gán một lambdachức năng cho bất cứ điều gì thì bạn đang làm sai và nên sử dụng defthay thế
Chris_Rands

1
@zgulser Đó không phải là một định nghĩa, nó chỉ là một tuyên bố về một cái gì đó mà lập trình chức năng cho phép bạn làm.
David Z

377

lambdachỉ là một cách nói ưa thích function. Khác với tên của nó, không có gì tối nghĩa, đáng sợ hoặc khó hiểu về nó. Khi bạn đọc dòng sau, thay thế lambdabằng functiontrong tâm trí của bạn:

>>> f = lambda x: x + 1
>>> f(3)
4

Nó chỉ định nghĩa một chức năng của x. Một số ngôn ngữ khác, như R, nói rõ ràng:

> f = function(x) { x + 1 }
> f(3)
4

Bạn thấy sao? Đó là một trong những điều tự nhiên nhất để làm trong lập trình.


36
Đây là một mô tả tuyệt vời cho những người đến từ nền tảng không lập trình (ví dụ: khoa học chính xác) làm cho ý nghĩa của lambda rất đơn giản để hiểu. Cảm ơn!
Gabriel

10
Raymond Hettinger than thở về cái tên trong một trong những cuộc nói chuyện của mình và nói rằng mọi sự nhầm lẫn có thể tránh được bằng cách đặt tên cho nó là 'tạo ra chức năng' thay vì 'lambda'. :-)
ankush981 7/07/2016

10
thay thế lambdabằng functiontrong tâm trí của bạn và thêm vào returntrước biểu hiện cuối cùng
Ciprian Tomoiagă

4
@AaronMcMillin Thử type(lambda x: 3). lambdabiểu thức và defcâu lệnh đều tạo ra functioncác đối tượng; nó chỉ là cú pháp của một lambdabiểu thức giới hạn những trường hợp nào nó có thể tạo ra.
chepner

6
@AaronMcMillin Bạn đang thiếu quan điểm của tôi. Chỉ vì bạn không thể xác định mọi hàm bằng một lambdabiểu thức không có nghĩa là kết quả của lambdabiểu thức không phải là một hàm.
chepner

107

Tóm tắt hai dòng:

  1. Đóng cửa : Rất hữu ích. Tìm hiểu chúng, sử dụng chúng, yêu chúng.
  2. lambdaTừ khóa của Python : không cần thiết, đôi khi hữu ích. Nếu bạn thấy mình làm bất cứ điều gì phức tạp từ xa với nó, hãy đặt nó đi và xác định một chức năng thực sự.

72

Lambda là một phần của cơ chế trừu tượng hóa rất quan trọng liên quan đến các hàm bậc cao hơn. Để hiểu đúng về giá trị của nó, vui lòng xem các bài học chất lượng cao từ Abelson và Sussman , và đọc cuốn sách SICP

Đây là những vấn đề có liên quan trong kinh doanh phần mềm hiện đại, và trở nên phổ biến hơn bao giờ hết.


1
Biểu thức Lambda cũng đang trở nên phổ biến trong các ngôn ngữ khác (như C #). Họ không đi đâu cả. Đọc về đóng cửa sẽ là một bài tập hữu ích để hiểu Lambdas. Việc đóng cửa tạo ra rất nhiều phép thuật mã hóa có thể có trong các khung như jQuery.
Dan Esparza

3
Đừng nhầm lẫn lambdacác chức năng hạng nhất và hạng nhất. Python có một lambdacâu lệnh cực kỳ hạn chế , nhưng đầy đủ các hàm hạng nhất. Sự khác biệt duy nhất mà nó tạo ra là bạn phải đặt tên cho các chức năng bạn muốn vượt qua.
Gareth Latty

59

lambdas cực kỳ hữu ích trong lập trình GUI. Ví dụ: giả sử bạn đang tạo một nhóm các nút và bạn muốn sử dụng một cuộc gọi lại được tham số hóa duy nhất thay vì một cuộc gọi lại duy nhất cho mỗi nút. Lambda cho phép bạn thực hiện điều đó một cách dễ dàng:

for value in ["one","two","three"]:
    b = tk.Button(label=value, command=lambda arg=value: my_callback(arg))
    b.pack()

(Lưu ý: mặc dù câu hỏi này được hỏi cụ thể lambda, bạn cũng có thể sử dụng funcools.partial để có cùng loại kết quả)

Cách khác là tạo một cuộc gọi lại riêng cho mỗi nút có thể dẫn đến mã trùng lặp.


1
Đây chính xác là lý do tại sao tôi tìm kiếm lambda là gì, nhưng tại sao nó hoạt động, với tôi nó trông giống hệt như chỉ gọi hàm thẳng ( stackoverflow.com/questions/3704568/ .) Có lẽ muộn rồi, nó vẫn hoạt động, nhưng tại sao nó lại hoạt động?
Rqomey

5
@Rqomey: sự khác biệt là trong ví dụ valuenày được định nghĩa trong một vòng lặp; trong ví dụ khác, tham số luôn chỉ có một giá trị. Khi bạn thêm một cái gì đó như arg=value, bạn đang đính kèm giá trị hiện tại vào cuộc gọi lại. Không có nó, bạn liên kết một tham chiếu đến biến trong cuộc gọi lại. Tham chiếu sẽ luôn chứa giá trị cuối cùng của biến, vì cuộc gọi lại xảy ra một thời gian sau khi vòng lặp đã chạy xong.
Bryan Oakley

1
Tôi mới làm việc này ngày hôm qua, thật lòng tôi không thể tin được nó hữu ích như thế nào ... Tôi có thể xây dựng một menu từ một vòng lặp và một tệp csv để cấu hình. Thứ thực sự hữu ích.
Rqomey

1
Lưu ý sự tồn tại của functools.partial()nó cho phép bạn làm điều này với ít hành trình (và không có lambda).
Gareth Latty

2
partial(my_callback, value)vs lambda arg=value: my_callback(arg)- lambda có nhiều hành trình hơn (gán cho arg và sau đó sử dụng) và nó không rõ ràng ý định là gì (bạn có thể đang làm một cái gì đó khác biệt tinh tế trong lambda). Nhập khẩu không thực sự là một vấn đề (dù sao bạn cũng có một đống và mỗi lần một tệp). Mã được đánh giá tốt nhất về cách nó đọc tốt, và partial()dễ đọc hơn nhiều so với lambda.
Gareth Latty

58

Tôi nghi ngờ lambda sẽ biến mất. Xem bài viết của Guido về việc cuối cùng từ bỏ việc cố gắng loại bỏ nó. Cũng thấy một phác thảo của cuộc xung đột .

Bạn có thể xem bài đăng này để biết thêm về lịch sử về thỏa thuận đằng sau các tính năng chức năng của Python: http://python-history.blogspot.com/2009/04/origins-of-pythons-feftal-features.html

Thật kỳ lạ, bản đồ, bộ lọc và giảm các chức năng ban đầu thúc đẩy sự ra đời của lambda và các tính năng chức năng khác phần lớn đã được thay thế bằng cách hiểu danh sách và biểu thức trình tạo. Trong thực tế, hàm less đã bị xóa khỏi danh sách các hàm dựng sẵn trong Python 3.0. (Tuy nhiên, không cần thiết phải gửi khiếu nại về việc xóa lambda, bản đồ hoặc bộ lọc: họ đang ở. :-)

Hai xu của riêng tôi: Hiếm khi lambda có giá trị như nó rõ ràng. Nói chung, có một giải pháp rõ ràng hơn không bao gồm lambda.


2
lưu ý rằng giảm vẫn có thể nhập được trong Python 3.0. Nếu bạn thực sự muốn nó, bạn vẫn có thể có nó.
Paweł Polewicz

Tôi nghĩ rằng nỗ lực của Guido là về cú pháp. Người này cũng nghĩ rằng: cackhanded.com/blog/post/2008/01/24/ từ
leewz

38

Trong Python, lambdachỉ là một cách xác định hàm nội tuyến,

a = lambda x: x + 1
print a(1)

và ..

def a(x): return x + 1
print a(1)

..are sự chính xác như vậy.

Bạn không thể làm gì với lambda mà bạn không thể làm với hàm thông thường, trong các hàm Python là một đối tượng giống như mọi thứ khác và lambdas chỉ cần định nghĩa một hàm:

>>> a = lambda x: x + 1
>>> type(a)
<type 'function'>

Tôi thành thật nghĩ rằng lambdatừ khóa là không cần thiết trong Python. Tôi chưa bao giờ có nhu cầu sử dụng chúng (hoặc thấy một từ được sử dụng trong đó một hàm thông thường, hiểu danh sách hoặc một trong nhiều hàm dựng sẵn có thể được sử dụng tốt hơn)

Đối với một ví dụ hoàn toàn ngẫu nhiên, từ bài viết "lambda của Python bị hỏng!" :

Để xem lambda bị hỏng như thế nào, hãy thử tạo một danh sách các hàm fs=[f0,...,f9]trong đó fi(n)=i+n. Nỗ lực đầu tiên:

>>> fs = [(lambda n: i + n) for i in range(10)]
>>> fs[3](4)
13

Tôi sẽ tranh luận, ngay cả khi điều đó đã làm việc, nó khủng khiếp và "unpythonic", chức năng tương tự có thể được viết theo vô số cách khác, ví dụ:

>>> n = 4
>>> [i + n for i in range(10)]
[4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Vâng, nó không giống nhau, nhưng tôi chưa bao giờ thấy nguyên nhân tạo ra một nhóm các hàm lambda trong danh sách được yêu cầu. Nó có thể có ý nghĩa trong các ngôn ngữ khác, nhưng Python không phải là Haskell (hoặc Lisp, hoặc ...)

Xin lưu ý rằng chúng tôi có thể sử dụng lambda và vẫn đạt được kết quả mong muốn theo cách này:

>>> fs = [(lambda n,i=i: i + n) for i in range(10)]
>>> fs[3](4)
7

Biên tập:

Có một vài trường hợp lambda hữu ích, ví dụ, nó thường thuận tiện khi kết nối tín hiệu trong các ứng dụng PyQt, như thế này:

w = PyQt4.QtGui.QLineEdit()
w.textChanged.connect(lambda event: dothing())

Chỉ cần làm w.textChanged.connect(dothing)sẽ gọi dothingphương thức với một eventđối số phụ và gây ra lỗi. Sử dụng lambda có nghĩa là chúng ta có thể nhanh chóng loại bỏ đối số mà không phải xác định hàm gói.


2
đối số "lambda bị hỏng" của bạn bị hỏng, do quy tắc phạm vi biến python hoạt động theo cách đó, giai đoạn. Bạn sẽ bị cắn theo cách tương tự nếu bạn tạo một bao đóng bên trong for-loop.
Antti Haapala

1
lambda chỉ là cách của python để cung cấp cho người dùng chức năng "ẩn danh", giống như nhiều ngôn ngữ khác có (ví dụ: javascript).
Stefan Gruenwald

1
Các lambda và chức năng amà bạn xác định không hoàn toàn giống nhau. :) Chúng khác nhau theo __name__lĩnh vực ít nhất ...
nperson325681

1
Nó hoạt động không chỉ là một chức năng nội tuyến.
kta

Lập luận của bạn về "có thể có ý nghĩa trong ngôn ngữ khác" là lạ. Hoặc có những phẩm chất nội tại của lambdas, hoặc chúng không có.
Titou

31

Tôi thấy lambda hữu ích cho một danh sách các chức năng làm tương tự, nhưng cho các trường hợp khác nhau.

Giống như các quy tắc số nhiều của Mozilla :

plural_rules = [
    lambda n: 'all',
    lambda n: 'singular' if n == 1 else 'plural',
    lambda n: 'singular' if 0 <= n <= 1 else 'plural',
    ...
]
# Call plural rule #1 with argument 4 to find out which sentence form to use.
plural_rule[1](4) # returns 'plural'

Nếu bạn phải xác định một chức năng cho tất cả những người bạn sẽ phát điên vào cuối của nó.
Ngoài ra, nó sẽ không được tốt đẹp với tên hàm như plural_rule_1, plural_rule_2vv Và bạn cần phải eval()nó khi bạn đang phụ thuộc vào một hàm biến id.


1
Điều này trông tương tự như các cuộc gặp gỡ ngắn ngủi mà tôi đã có trong F # với các tùy chọn và kết hợp mẫu. Bạn có thêm thông tin về cách sử dụng cú pháp này không?
Ken

26

Khá nhiều thứ bạn có thể làm với lambdabạn có thể làm tốt hơn với các hàm được liệt kê hoặc các biểu thức danh sách và trình tạo.

Do đó, đối với hầu hết các phần, bạn chỉ nên là một trong những người trong bất kỳ tình huống nào (ngoại trừ có thể là mã đầu được viết trong trình thông dịch tương tác).


3
"Phần lớn bạn chỉ nên là một trong những người trong mọi tình huống". Gõ lambda trong trình thông dịch thậm chí không hữu ích.
S.Lott

11
@Javier Tôi đồng ý với bạn nếu bạn đang nói về khái niệm "lambda"; tuy nhiên, nếu chúng ta đang nói về "lambda" thì từ khóa python thì: 1) các hàm được đặt tên nhanh hơn và có thể làm được nhiều hơn (câu lệnh + biểu thức) gần như bất cứ nơi nào bạn sẽ sử dụng lambdas (ánh xạ + bộ lọc), bạn chỉ có thể tạo biểu thức hoặc hiểu danh sách đó là hiệu suất cao hơn và súc tích. Tôi không nói rằng các hàm hạng nhất không thú vị, chỉ là từ khóa "lambda" trong python không tốt bằng việc sử dụng một hàm có tên, chỉ vậy thôi.
Aaron Maenpaa

3
lambda đã không thể thiếu đối với tôi để sử dụng với các hàm lấy các đối số gọi lại như key = argument to sort () và sort ()
Rick Copeland

19
@Rick Tôi không nghi ngờ điều đó, nhưng thực tế là nếu bạn nhìn thấy "lambda" và bạn nghĩ "zohmygod lambda" và bắt đầu viết mã chương trình bằng python, bạn chắc chắn sẽ thất vọng vì những hạn chế của biểu hiện lambda của python. Mặt khác, nếu bạn bắt đầu tự suy nghĩ "Liệu một sự hiểu biết danh sách có hoạt động không? Không. Tôi sẽ cần gì để trở thành một chức năng được đặt tên? Không. Được rồi: được sắp xếp (xs, key = lambda x: x.name, x.height) ", có thể bạn sẽ kết thúc bằng lambda đúng số lần.
Aaron Maenpaa

4
+1: Tôi không thể nhấn mạnh đến mức đủ khi một người sử dụng lambda, người ta sử dụng chức năng không tên . Và tên có giá trị gia tăng trí tuệ quý giá.
Stephane Rolland

19

Tôi đã sử dụng Python được vài năm và tôi chưa bao giờ gặp phải trường hợp nào tôi cần lambda. Thực sự, như hướng dẫn nêu, nó chỉ dành cho đường cú pháp.


8
Chúng rất hữu ích khi phát triển GUI bằng python. Thông thường, các widget cần một tham chiếu đến một chức năng. Nếu bạn cần một widget để gọi một hàm và truyền đối số, lambda là một cách rất thuận tiện để làm điều đó.
Bryan Oakley

1
Lợi thế của việc chuyển nhiều đối số vào hàm là gì? func_name (a, b): trả về a + b thay vì sử dụng lambda
dter

2
nó không cần thiết nhưng nó giúp viết mã ngắn hơn và dễ đọc hơn trong nhiều trường hợp, đặc biệt. trong các ngôn ngữ dài dòng như Java hoặc C ++
phuclv

17

Tôi không thể nói với việc triển khai lambda cụ thể của python, nhưng nói chung các chức năng lambda thực sự tiện dụng. Chúng là một kỹ thuật cốt lõi (thậm chí có thể là kỹ thuật) của lập trình chức năng và chúng cũng rất hữu dụng trong các chương trình hướng đối tượng. Đối với một số loại vấn đề nhất định, chúng là giải pháp tốt nhất, vì vậy chắc chắn không nên quên!

Tôi khuyên bạn nên đọc về các bao đóngchức năng bản đồ (liên kết đến các tài liệu python, nhưng nó tồn tại trong gần như mọi ngôn ngữ hỗ trợ các cấu trúc chức năng) để xem tại sao nó hữu ích.


3
Những thứ đó có thể được thực hiện mà không có lambdas. Đó chỉ là một rắc rối lớn.
Brian

17

Chức năng Lambda đó là một cách không quan liêu để tạo ra một chức năng.

Đó là nó. Ví dụ: giả sử bạn có chức năng chính và cần bình phương giá trị. Chúng ta hãy xem cách truyền thống và cách lambda để làm điều này:

Theo cách truyền thống:

def main():
...
...
y = square(some_number)
...
return something

def square(x):
    return x**2

Cách lambda:

def main():
...
square = lambda x: x**2
y = square(some_number)
return something

Thấy sự khác biệt?

Các chức năng Lambda rất phù hợp với danh sách, như danh sách hiểu hoặc bản đồ. Trong thực tế, liệt kê danh sách đó là một cách "pythonic" để thể hiện bản thân bằng lambda. Ví dụ:

>>>a = [1,2,3,4]
>>>[x**2 for x in a]
[1,4,9,16]

Chúng ta hãy xem từng yếu tố của cú pháp có nghĩa là gì:

[]: "Cho tôi một danh sách"

x ** 2: "sử dụng chức năng mới sinh này"

cho x trong a: "vào từng phần tử trong một"

Thật tiện lợi phải không? Tạo các chức năng như thế này. Hãy viết lại bằng lambda:

>>> square = lambda x: x**2
>>> [square(s) for x in a]
[1,4,9,16]

Bây giờ chúng ta hãy sử dụng bản đồ, đó là điều tương tự, nhưng trung lập về ngôn ngữ hơn. Bản đồ có 2 đối số:

(i) một chức năng

(ii) lặp lại

Và cung cấp cho bạn một danh sách trong đó mỗi phần tử là hàm được áp dụng cho từng phần tử của lần lặp.

Vì vậy, sử dụng bản đồ, chúng ta sẽ có:

>>> a = [1,2,3,4]
>>> squared_list = map(lambda x: x**2, a)

Nếu bạn thành thạo lambdas và lập bản đồ, bạn sẽ có một sức mạnh tuyệt vời để thao tác dữ liệu và một cách ngắn gọn. Các hàm Lambda không tối nghĩa cũng không lấy đi sự rõ ràng của mã. Đừng nhầm lẫn một cái gì đó khó khăn với một cái gì đó mới. Một khi bạn bắt đầu sử dụng chúng, bạn sẽ thấy nó rất rõ ràng.


13

Một trong những điều tốt đẹp về lambdađiều đó theo ý kiến ​​của tôi, đó là cách trì hoãn đánh giá cho các hình thức đơn giản cho đến khi giá trị là cần thiết. Hãy để tôi giải thích.

Nhiều thói quen của thư viện được triển khai sao cho chúng cho phép các tham số nhất định có thể gọi được (trong đó lambda là một). Ý tưởng là giá trị thực tế sẽ chỉ được tính vào thời điểm nó sẽ được sử dụng (thay vì khi nó được gọi). Một ví dụ (giả định) có thể giúp minh họa điểm. Giả sử bạn có một thói quen sẽ ghi nhật ký một dấu thời gian nhất định. Bạn muốn thói quen sử dụng thời gian hiện tại trừ đi 30 phút. Bạn sẽ gọi nó như vậy

log_timestamp(datetime.datetime.now() - datetime.timedelta(minutes = 30))

Bây giờ giả sử hàm thực tế sẽ chỉ được gọi khi một sự kiện nào đó xảy ra và bạn muốn dấu thời gian chỉ được tính vào thời điểm đó. Bạn có thể làm điều này như vậy

log_timestamp(lambda : datetime.datetime.now() - datetime.timedelta(minutes = 30))

Giả sử log_timestampcó thể xử lý các cuộc gọi như thế này, nó sẽ đánh giá điều này khi cần và bạn sẽ có dấu thời gian tại thời điểm đó.

Tất nhiên có nhiều cách khác để làm điều này ( operatorví dụ sử dụng mô-đun) nhưng tôi hy vọng tôi đã truyền đạt được quan điểm.

Cập nhật : Đây là một ví dụ thực tế cụ thể hơn một chút.

Cập nhật 2 : Tôi nghĩ rằng đây là một ví dụ về cái được gọi là thunk .


11

Như đã nêu ở trên, toán tử lambda trong Python định nghĩa một hàm ẩn danh và trong các hàm Python là các bao đóng. Điều quan trọng là không nhầm lẫn giữa khái niệm đóng cửa với toán tử lambda, mà chỉ đơn thuần là cú pháp methadone cho họ.

Khi tôi bắt đầu sử dụng Python vài năm trước, tôi đã sử dụng lambdas rất nhiều, nghĩ rằng chúng rất tuyệt, cùng với việc hiểu danh sách. Tuy nhiên, tôi đã viết và phải duy trì một trang web lớn được viết bằng Python, với thứ tự vài nghìn điểm chức năng. Tôi đã học được từ kinh nghiệm rằng lambdas có thể ổn với những thứ nguyên mẫu, nhưng không cung cấp gì qua các hàm nội tuyến (được đặt tên là đóng) ngoại trừ việc lưu một vài câu đố chính, hoặc đôi khi không.

Về cơ bản điều này sôi xuống đến một số điểm:

  • dễ dàng hơn để đọc phần mềm được viết rõ ràng bằng cách sử dụng tên có ý nghĩa. Đóng cửa ẩn danh theo định nghĩa không thể có một tên có ý nghĩa, vì chúng không có tên. Sự ngắn gọn này dường như, vì một số lý do, cũng lây nhiễm các tham số lambda, do đó chúng ta thường thấy các ví dụ như lambda x: x + 1
  • việc sử dụng lại các bao đóng có tên dễ dàng hơn, vì chúng có thể được gọi bằng tên nhiều lần, khi có một tên để gọi chúng theo.
  • dễ dàng hơn để gỡ lỗi mã đang sử dụng các bao đóng có tên thay vì lambdas, vì tên này sẽ xuất hiện trong tracebacks và xung quanh lỗi.

Đó là đủ lý do để làm tròn chúng và chuyển đổi chúng thành các bao đóng có tên. Tuy nhiên, tôi giữ hai mối hận thù khác đối với việc đóng cửa ẩn danh.

Mối hận thù đầu tiên chỉ đơn giản là chúng chỉ là một từ khóa không cần thiết khác làm lộn xộn ngôn ngữ.

Mối hận thù thứ hai sâu sắc hơn và ở cấp độ mô hình, tức là tôi không thích rằng họ quảng bá một phong cách lập trình chức năng, bởi vì phong cách đó kém linh hoạt hơn so với việc truyền thông điệp, hướng đối tượng hoặc phong cách thủ tục, bởi vì tính toán lambda không phải là Turing- hoàn thành (may mắn là trong Python, chúng ta vẫn có thể thoát ra khỏi giới hạn đó ngay cả trong lambda). Những lý do tôi cảm thấy lambdas thúc đẩy phong cách này là:

  • Có một sự trở lại ngầm, tức là chúng có vẻ như chúng 'nên' là các hàm.

  • Chúng là một cơ chế ẩn trạng thái thay thế cho một cơ chế khác, rõ ràng hơn, dễ đọc hơn, dễ sử dụng hơn và tổng quát hơn: các phương thức.

Tôi cố gắng hết sức để viết Python không có lambda và loại bỏ lambdas. Tôi nghĩ Python sẽ là một ngôn ngữ tốt hơn một chút nếu không có lambdas, nhưng đó chỉ là ý kiến ​​của tôi.


1
"... Trong các hàm Python là các bao đóng". Điều đó không hoàn toàn đúng, như tôi hiểu. Đóng cửa là chức năng, nhưng chức năng không phải luôn luôn đóng cửa. Hàm-> lambda x, y: x + y. Đóng cửa-> lambda x: lambda y: x + y
0atman

6
"bởi vì tính toán lambda không hoàn thành Turing" hoàn toàn sai, tính toán lambda chưa được xử lý IS Turing hoàn tất, đó là lý do tại sao nó rất quan trọng. Bạn có thể nhận được đệ quy bằng cách sử dụng bộ kết hợp Y,Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
Antti Haapala

Hơn nữa, nếu một người vào Wikipedia để đọc về tính đầy đủ của Turing, thì nó nói "Một ví dụ kinh điển là phép tính lambda".
Antti Haapala

nghiêm túc - Không hoàn thành Turing - câu trả lời này cần chỉnh sửa hoặc rút lại nghiêm túc.
Tony Suffolk 66

@ MarcinŁoś Được bình chọn vì nó tuân thủ KISS. Để so sánh, cuốn sách K & R đề cập rằng trong khi sử dụng phép gán như một phần hoặc biểu thức lớn hơn thì gọn hơn, nó thường ít đọc hơn. Xu hướng của việc sử dụng các mẫu lập trình chức năng là trite và sáo rỗng. Nó là đủ để nói làm thế nào họ có thể được sử dụng, nhưng nó quá nhiệt tình để nói rằng họ là tối quan trọng để trở thành một nhà phát triển có thẩm quyền; Hoàn toàn chủ quan. Đối số này tương tự như các nhà phát triển C ++, những người lập luận rằng các ngôn ngữ không có lớp nguyên thủy, kém hơn và không đầy đủ. "Turing-đầy đủ", các đối số là mô phạm.
typedeaf

11

Lambdas thực sự là những cấu trúc rất mạnh mẽ xuất phát từ ý tưởng trong lập trình chức năng và đó là thứ mà không có cách nào dễ dàng sửa đổi, định nghĩa lại hoặc loại bỏ trong tương lai gần của Python. Chúng giúp bạn viết mã mạnh hơn vì nó cho phép bạn truyền các hàm dưới dạng tham số, do đó, ý tưởng về các hàm là công dân hạng nhất.

Lambdas có xu hướng gây nhầm lẫn, nhưng một khi có được sự hiểu biết vững chắc, bạn có thể viết mã thanh lịch sạch sẽ như thế này:

squared = map(lambda x: x*x, [1, 2, 3, 4, 5])

Dòng mã trên trả về một danh sách các bình phương của các số trong danh sách. Tất nhiên, bạn cũng có thể làm như sau:

def square(x):
    return x*x

squared = map(square, [1, 2, 3, 4, 5])

Rõ ràng mã cũ ngắn hơn và điều này đặc biệt đúng nếu bạn định sử dụng chức năng bản đồ (hoặc bất kỳ chức năng tương tự nào có chức năng làm tham số) chỉ ở một nơi. Điều này cũng làm cho mã trực quan và thanh lịch hơn.

Ngoài ra, như @David Zaslavsky đã đề cập trong câu trả lời của mình, việc hiểu danh sách không phải lúc nào cũng là cách tốt nhất là nếu danh sách của bạn phải nhận các giá trị từ một cách toán học tối nghĩa.

Từ quan điểm thực tế hơn, một trong những lợi thế lớn nhất của lambdas đối với tôi gần đây là về GUI và lập trình hướng sự kiện. Nếu bạn xem qua các cuộc gọi lại trong Tkinter, tất cả những gì họ lấy làm đối số là sự kiện đã kích hoạt chúng. Ví dụ

def define_bindings(widget):
    widget.bind("<Button-1>", do-something-cool)

def do-something-cool(event):
    #Your code to execute on the event trigger

Bây giờ nếu bạn có một số đối số để vượt qua thì sao? Một cái gì đó đơn giản như truyền 2 đối số để lưu trữ tọa độ của một lần nhấp chuột. Bạn có thể dễ dàng làm điều đó như thế này:

def main():
    # define widgets and other imp stuff
    x, y = None, None
    widget.bind("<Button-1>", lambda event: do-something-cool(x, y))

def do-something-cool(event, x, y):
    x = event.x
    y = event.y
    #Do other cool stuff

Bây giờ bạn có thể lập luận rằng điều này có thể được thực hiện bằng cách sử dụng các biến toàn cục, nhưng bạn có thực sự muốn đập đầu lo lắng về quản lý bộ nhớ và rò rỉ đặc biệt là nếu biến toàn cục sẽ chỉ được sử dụng ở một nơi cụ thể? Đó sẽ chỉ là phong cách lập trình kém.

Nói tóm lại, lambdas là tuyệt vời và không bao giờ nên được đánh giá thấp. Lambdas Python không giống như lambdas LISP (mạnh hơn), nhưng bạn thực sự có thể làm rất nhiều thứ ma thuật với chúng.


Cảm ơn. Tôi hoàn toàn không hiểu ví dụ cuối cùng của bạn. Làm thế nào đến x và y được định nghĩa trong cả hai maindo_something_cool? Điều gì xảy ra xytrong chức năng? Các giá trị thông qua dường như được ghi đè ngay lập tức? Làm thế nào để chức năng biết về event? Bạn có thể thêm một số ý kiến ​​/ giải thích? Cảm ơn
Sanjay Manohar

@SanjayManohar Tôi đang chuyển xvà các yđối số do-something-coolvà các giá trị của chúng đang được đặt trong hàm để minh họa cách bạn có thể sử dụng lambdas để truyền các đối số mà không mong đợi. Các widget.bindchức năng hy vọng một eventtham số trong đó xác định các sự kiện GUI trên rằng phụ tùng đặc biệt. Tôi khuyên bạn nên đọc trên mô hình lập trình của Tkinter để rõ ràng hơn.
varagrawal

hmm tôi nghĩ rằng tôi hiểu mô hình Tkinter. Nhưng vẫn không hiểu lắm - bạn vượt qua x,yrồi x=event.x. Điều đó không ghi đè lên giá trị bạn đã vượt qua? Và làm thế nào để chức năng biết những gì eventlà? Tôi không thấy nơi bạn chuyển nó đến chức năng. Hay là một phương pháp? Ngoài ra, bạn có được phép trừ dấu trong một tên chức năng?
Sanjay Manohar

@SanjayManohar bạn thân bạn cần đọc lên Tkinter và Python. Tkinter truyền đối tượng sự kiện vào chức năng như một hành động mặc định. Đối với x, y, đó chỉ là một ví dụ cho mục đích minh họa. Tôi đang cố gắng thể hiện sức mạnh của lambdas chứ không phải Tkinter. :)
varagrawal

1
OK bây giờ tôi thấy những gì bạn dự định - bạn muốn chức năng nhận được event? trong trường hợp đó, lambda của bạn không nên đọc lambda event: do_something_cool(event,x,y)?
Sanjay Manohar

8

Lambdas được liên kết sâu sắc với phong cách lập trình chức năng nói chung. Ý tưởng rằng bạn có thể giải quyết vấn đề bằng cách áp dụng một hàm cho một số dữ liệu và hợp nhất các kết quả, là những gì google sử dụng để thực hiện hầu hết các thuật toán của nó.

Các chương trình được viết theo phong cách lập trình chức năng, dễ dàng được song song hóa và do đó ngày càng trở nên quan trọng hơn với các máy đa lõi hiện đại. Vì vậy, trong ngắn hạn, KHÔNG bạn không nên quên chúng.


7

Chúc mừng đầu tiên đã tìm ra lambda. Theo tôi đây là công trình thực sự mạnh mẽ để hành động. Xu hướng ngày nay đối với các ngôn ngữ lập trình chức năng chắc chắn là một chỉ số cho thấy không nên tránh và cũng sẽ không được xác định lại trong tương lai gần.

Bạn chỉ cần suy nghĩ một chút khác nhau. Tôi chắc chắn bạn sẽ sớm yêu nó. Nhưng hãy cẩn thận nếu bạn chỉ đối phó với trăn. Bởi vì lambda không phải là một đóng cửa thực sự, nó bị "phá vỡ" bằng cách nào đó: pythons lambda bị hỏng


Lambda của Python không bị hỏng. Có hai cách để xử lý người dân địa phương ở lambdas. Cả hai đều có ưu điểm và nhược điểm. Tôi nghĩ rằng cách tiếp cận Python (và C #) có lẽ trái ngược với những ngôn ngữ quen thuộc hơn với ngôn ngữ chức năng thuần túy, vì với ngôn ngữ thuần túy chức năng tôi không nghĩ cách tiếp cận đó thậm chí còn có ý nghĩa.
Brian

Nó thực sự là phản trực giác. Tôi không phải là một lập trình viên python nhưng trong squeak smalltalk thì nó cũng như vậy và tôi thường xuyên vấp phải điều này. Vì vậy, ngay cả tôi cũng sẽ coi nó là "hỏng" :)
Norbert Hartl

Không, điều này không liên quan gì đến tính phản tác dụng. Nó chỉ là phạm vi từ vựng của các tên biến là của một hàm; một vòng lặp không giới thiệu một phạm vi từ vựng. Các chức năng cũng hoạt động theo cách tương tự trong Javascript. Nếu bạn cần một phạm vi cho var, bạn luôn có thể làm (lambda scopedvar: lambda x: scopedvar + x) ()
Antti Haapala

6

Tôi mới bắt đầu Python và chạy đầu tiên vào Lambda - điều này khiến tôi mất một lúc để tìm ra.

Lưu ý rằng đây không phải là một sự lên án của bất cứ điều gì. Mỗi người có một bộ những thứ khác nhau không dễ dàng đến.

Có phải lambda là một trong những món đồ ngôn ngữ 'thú vị' mà trong đời thực nên bị lãng quên?

Không.

Tôi chắc chắn có một số trường hợp cạnh có thể cần thiết, nhưng với sự tối nghĩa của nó,

Nó không tối nghĩa. Hai đội trước đây tôi từng làm việc, mọi người đều sử dụng tính năng này mọi lúc.

tiềm năng của nó được xác định lại trong các bản phát hành trong tương lai (giả định của tôi dựa trên các định nghĩa khác nhau của nó)

Tôi đã thấy không có đề xuất nghiêm túc nào để xác định lại nó trong Python, ngoài việc sửa chữa ngữ nghĩa đóng cửa một vài năm trước.

và độ rõ của mã hóa giảm - có nên tránh?

Nó không kém rõ ràng, nếu bạn đang sử dụng đúng. Ngược lại, có nhiều cấu trúc ngôn ngữ có sẵn làm tăng sự rõ ràng.

Điều này nhắc nhở tôi về việc tràn (tràn bộ đệm) các loại C - chỉ vào biến trên cùng và quá tải để đặt các giá trị trường khác ... sắp xếp một trình diễn kỹ thuật nhưng cơn ác mộng bảo trì mã hóa ..

Lambda giống như tràn bộ đệm? Ồ Tôi không thể tưởng tượng bạn đang sử dụng lambda như thế nào nếu bạn nghĩ đó là "cơn ác mộng bảo trì".


5
-1 vì đã khiến tôi (và những người khác) đọc lại toàn bộ câu hỏi. Lưu ý rằng những người khác quản lý để trả lời mà không làm điều đó.
Paweł Polewicz

6

Tôi sử dụng lambdas để tránh sao chép mã. Nó sẽ làm cho chức năng dễ hiểu dễ hiểu Eg:

def a_func()
  ...
  if some_conditon:
     ...
     call_some_big_func(arg1, arg2, arg3, arg4...)
  else
     ...
     call_some_big_func(arg1, arg2, arg3, arg4...)

Tôi thay thế nó bằng lambda tạm thời

def a_func()
  ...
  call_big_f = lambda args_that_change: call_some_big_func(arg1, arg2, arg3, args_that_change)
  if some_conditon:
     ...
     call_big_f(argX)
  else
     ...
     call_big_f(argY)

5

Tôi bắt đầu đọc cuốn sách của David Mertz hôm nay 'Xử lý văn bản bằng Python.' Trong khi anh ta có một mô tả khá ngắn gọn về Lambda, các ví dụ trong chương đầu tiên kết hợp với lời giải thích trong Phụ lục A đã khiến họ nhảy ra khỏi trang cho tôi (cuối cùng) và đột nhiên tôi hiểu giá trị của chúng. Điều đó không có nghĩa là lời giải thích của anh ấy sẽ có hiệu quả với bạn và tôi vẫn đang ở giai đoạn khám phá nên tôi sẽ không cố gắng thêm vào những phản hồi này ngoài những điều sau: Tôi mới biết về Python Tôi mới biết về OOP Lambdas là một cuộc đấu tranh đối với tôi Bây giờ tôi đã đọc Mertz, tôi nghĩ rằng tôi có được chúng và tôi thấy chúng rất hữu ích vì tôi nghĩ chúng cho phép một cách tiếp cận sạch hơn để lập trình.

Anh ta tái tạo Zen của Python, một trong số đó là Simple thì tốt hơn phức tạp. Là một lập trình viên không phải OOP đang đọc mã với lambdas (và cho đến tuần trước việc hiểu danh sách) tôi đã nghĩ- Điều này có đơn giản không? . Cuối cùng tôi đã nhận ra rằng thực sự các tính năng này làm cho mã dễ đọc hơn và dễ hiểu hơn so với giải pháp thay thế - luôn luôn là một vòng lặp của một loại nào đó. Tôi cũng nhận ra rằng giống như báo cáo tài chính - Python không được thiết kế cho người dùng mới làm quen, thay vào đó nó được thiết kế cho người dùng muốn được giáo dục. Tôi không thể tin được ngôn ngữ này mạnh đến mức nào. Khi nó chợt nhận ra tôi (cuối cùng) mục đích và giá trị của lambdas tôi muốn xé ra khoảng 30 chương trình và bắt đầu đưa vào lambdas khi thích hợp.


5

Một trường hợp hữu ích cho việc sử dụng lambdas là cải thiện khả năng đọc các danh sách dài . Trong ví dụ loop_dicnày là ngắn gọn cho rõ ràng nhưng tưởng tượng loop_diclà rất dài. Nếu bạn chỉ sử dụng một giá trị đơn giản bao gồm ithay vì phiên bản lambda của giá trị đó, bạn sẽ nhận được a NameError.

>>> lis = [{"name": "Peter"}, {"name": "Josef"}]

>>> loop_dic = lambda i: {"name": i["name"] + " Wallace" }
>>> new_lis = [loop_dic(i) for i in lis]

>>> new_lis
[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]

Thay vì

>>> lis = [{"name": "Peter"}, {"name": "Josef"}]

>>> new_lis = [{"name": i["name"] + " Wallace"} for i in lis]

>>> new_lis
[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]

5

Tôi có thể cho bạn một ví dụ mà tôi thực sự cần lambda nghiêm túc. Tôi đang tạo một chương trình đồ họa, trong đó việc sử dụng nhấp chuột phải vào một tệp và gán cho nó một trong ba tùy chọn. Hóa ra trong Tkinter (chương trình giao diện GUI tôi đang viết cái này), khi ai đó nhấn nút, nó không thể được gán cho một lệnh lấy các đối số. Vì vậy, nếu tôi chọn một trong các tùy chọn và muốn kết quả của lựa chọn của mình là:

print 'hi there'

Sau đó không có vấn đề lớn. Nhưng nếu tôi cần sự lựa chọn của tôi để có một chi tiết cụ thể. Ví dụ: nếu tôi chọn lựa chọn A, nó gọi một hàm có một số đối số phụ thuộc vào lựa chọn A, B hoặc C, TKinter không thể hỗ trợ điều này. Lamda là lựa chọn duy nhất để giải quyết vấn đề này ...


2
Chà, có lẽ bạn có thể đã làm def foo...và sau đó chuyển qua foothay vì lambda. Nó chỉ là nhiều mã hơn và bạn phải đưa ra một cái tên.
Jon Coombs

4

Tôi sử dụng nó khá thường xuyên, chủ yếu là một đối tượng null hoặc để liên kết một phần các tham số với một chức năng.

Dưới đây là ví dụ:

để thực hiện mẫu đối tượng null:

{
    DATA_PACKET: self.handle_data_packets
    NET_PACKET: self.handle_hardware_packets
}.get(packet_type, lambda x : None)(payload)

để liên kết tham số:

giả sử tôi có API sau

def dump_hex(file, var)
    # some code
    pass

class X(object):
    #...
    def packet_received(data):
        # some kind of preprocessing
        self.callback(data)
    #...

Sau đó, khi tôi không nhanh chóng chuyển dữ liệu nhận được vào một tệp, tôi sẽ làm điều đó:

dump_file = file('hex_dump.txt','w')
X.callback = lambda (x): dump_hex(dump_file, x)
...
dump_file.close()

4

Tôi sử dụng lambdađể tạo các cuộc gọi lại bao gồm các tham số. Nó sạch hơn khi viết lambda trong một dòng hơn là viết một phương thức để thực hiện cùng chức năng.

Ví dụ:

import imported.module

def func():
    return lambda: imported.module.method("foo", "bar")

như trái ngược với:

import imported.module

def func():
    def cb():
        return imported.module.method("foo", "bar")
    return cb

4

Tôi là người mới bắt đầu với trăn, vì vậy để hiểu rõ hơn về lambda tôi đã so sánh nó với vòng lặp 'for'; về mặt hiệu quả. Đây là mã (python 2.7) -

import time
start = time.time() # Measure the time taken for execution

def first():
    squares = map(lambda x: x**2, range(10))
    # ^ Lambda
    end = time.time()
    elapsed = end - start
    print elapsed + ' seconds'
    return elapsed # gives 0.0 seconds

def second():
    lst = []
    for i in range(10):
        lst.append(i**2)
    # ^ a 'for' loop
    end = time.time()
    elapsed = end - start
    print elapsed + ' seconds'
    return elapsed # gives 0.0019998550415 seconds.

print abs(second() - first()) # Gives 0.0019998550415 seconds!(duh)

6
Bạn có thể quan tâm đến timeitmô-đun, thường cho kết quả chính xác hơn trừ time.time()các giá trị.
Kevin

2
Hmm, bạn không cần phải khởi động lại bộ đếm thời gian của mình khi bắt đầu đầu tiên () và thứ hai ()?
qneill

2

Lambda là một nhà xây dựng thủ tục. Bạn có thể tổng hợp các chương trình trong thời gian chạy, mặc dù lambda của Python không mạnh lắm. Lưu ý rằng ít người hiểu rằng loại lập trình.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.