Cú pháp đằng sau được sắp xếp (key = lambda: Bắn)


152

Tôi không hiểu cú pháp đằng sau sorted()đối số:

key=lambda variable: variable[0]

Không lambda độc đoán sao? Tại sao được variablenêu hai lần trong những gì trông giống như một dict?

Câu trả lời:


162

keylà một chức năng sẽ được gọi để chuyển đổi các mục của bộ sưu tập trước khi chúng được so sánh. Tham số được truyền chokey phải là một cái gì đó có thể gọi được.

Việc sử dụng lambdatạo một hàm ẩn danh (có thể gọi được). Trong trường hợp có sortedthể gọi được chỉ mất một tham số. Python lambdalà khá đơn giản. Nó chỉ có thể làm và trả lại một điều thực sự.

Cú pháp của lambdalà từ lambdatheo sau là danh sách các tên tham số sau đó là một khối mã. Danh sách tham số và khối mã được mô tả bằng dấu hai chấm. Điều này tương tự cấu trúc khác trong python cũng như while, for,if và vân vân. Chúng là tất cả các câu lệnh thường có một khối mã. Lambda chỉ là một ví dụ khác của một câu lệnh với một khối mã.

Chúng ta có thể so sánh việc sử dụng lambda với def để tạo hàm.

adder_lambda = lambda parameter1,parameter2: parameter1+parameter2
def adder_regular(parameter1, parameter2): return parameter1+parameter2

lambda chỉ cho chúng ta một cách để làm điều này mà không cần gán tên. Điều này làm cho nó tuyệt vời để sử dụng như là một tham số cho một chức năng.

variable được sử dụng hai lần ở đây vì ở bên trái dấu hai chấm, đó là tên của một tham số và ở phía bên phải, nó đang được sử dụng trong khối mã để tính toán một cái gì đó.


10
lưu ý (với OP): nên tránh gán lambda cho tên (nghĩa là sử dụng nó theo cách khác với chức năng ẩn danh). Nếu bạn thấy mình làm điều này có lẽ bạn chỉ nên sử dụng một def.
wim

151

Tôi nghĩ rằng tất cả các câu trả lời ở đây bao gồm cốt lõi của chức năng lambda trong bối cảnh được sắp xếp () khá độc đáo, tuy nhiên tôi vẫn cảm thấy như một mô tả dẫn đến sự hiểu biết trực quan còn thiếu, vì vậy đây là hai xu của tôi.

Để hoàn thiện, tôi sẽ nêu rõ mặt trước: sort () trả về một danh sách các phần tử được sắp xếp và nếu chúng ta muốn sắp xếp theo một cách cụ thể hoặc nếu chúng ta muốn sắp xếp một danh sách các phần tử phức tạp (ví dụ: danh sách lồng nhau hoặc một danh sách các bộ dữ liệu) chúng ta có thể gọi đối số chính.

Đối với tôi, sự hiểu biết trực quan về đối số chính, tại sao nó phải có thể gọi được và việc sử dụng lambda làm hàm có thể gọi được (ẩn danh) để thực hiện điều này có hai phần.

  1. Sử dụng lamba cuối cùng có nghĩa là bạn không phải viết (xác định) toàn bộ một chức năng, giống như một sblom được cung cấp một ví dụ về. Các hàm Lambda được tạo, sử dụng và bị hủy ngay lập tức - vì vậy chúng không tạo ra mã của bạn với nhiều mã sẽ chỉ được sử dụng một lần. Theo tôi hiểu thì đây là tiện ích cốt lõi của chức năng lambda và các ứng dụng của nó cho các vai trò như vậy rất rộng. Cú pháp của nó hoàn toàn theo quy ước, về bản chất là cú pháp lập trình nói chung. Tìm hiểu cú pháp và được thực hiện với nó.

Cú pháp Lambda như sau:

lambda input_variable (s) : ngon một lót

ví dụ

In [1]: f00 = lambda x: x/2

In [2]: f00(10)
Out[2]: 5.0

In [3]: (lambda x: x/2)(10)
Out[3]: 5.0

In [4]: (lambda x, y: x / y)(10, 2)
Out[4]: 5.0

In [5]: (lambda: 'amazing lambda')() # func with no args!
Out[5]: 'amazing lambda'
  1. Ý tưởng đằng sau keyđối số là nó sẽ đưa vào một tập hợp các hướng dẫn về cơ bản sẽ trỏ hàm 'sort ()' vào các phần tử danh sách nên được sử dụng để sắp xếp theo. Khi nó nói key=, điều thực sự có nghĩa là: Khi tôi lặp qua danh sách một phần tử tại một thời điểm (nghĩa là cho e trong danh sách), tôi sẽ chuyển phần tử hiện tại cho hàm tôi cung cấp trong đối số khóa và sử dụng hàm đó để tạo một danh sách được chuyển đổi sẽ thông báo cho tôi về thứ tự của danh sách được sắp xếp cuối cùng.

Kiểm tra xem nó:

mylist = [3,6,3,2,4,8,23]
sorted(mylist, key=WhatToSortBy)

Ví dụ cơ bản:

sorted(mylist)

[2, 3, 3, 4, 6, 8, 23] # tất cả các số theo thứ tự từ nhỏ đến lớn.

Ví dụ 1:

mylist = [3,6,3,2,4,8,23]
sorted(mylist, key=lambda x: x%2==0)

[3, 3, 23, 6, 2, 4, 8] # Kết quả được sắp xếp này có hợp lý với bạn không?

Lưu ý rằng hàm lambda của tôi được sắp xếp để kiểm tra xem (e) là chẵn hay lẻ trước khi sắp xếp.

NHƯNG CHỜ ĐỢI! Bạn có thể (hoặc có lẽ nên) tự hỏi hai điều - đầu tiên, tại sao tỷ lệ cược của tôi lại xuất hiện trước evens của tôi (vì giá trị khóa của tôi dường như đang nói với chức năng được sắp xếp của tôi để ưu tiên evens bằng cách sử dụng toán tử mod trongx%2==0 ). Thứ hai, tại sao evens của tôi ra khỏi trật tự? 2 đến trước 6 phải không? Bằng cách phân tích kết quả này, chúng ta sẽ tìm hiểu một cái gì đó sâu hơn về cách đối số được sắp xếp () 'key' hoạt động, đặc biệt là kết hợp với hàm lambda ẩn danh.

Đầu tiên, bạn sẽ nhận thấy rằng trong khi tỷ lệ cược đến trước khi phát sinh, bản thân các evens không được sắp xếp. Tại sao lại thế này ?? Hãy đọc tài liệu :

Các hàm chính Bắt đầu với Python 2.4, cả list.sort () và sort () đã thêm một tham số khóa để chỉ định một hàm được gọi trên mỗi phần tử danh sách trước khi so sánh.

Chúng ta phải đọc một chút giữa các dòng ở đây, nhưng điều này cho chúng ta biết là hàm sắp xếp chỉ được gọi một lần và nếu chúng ta chỉ định đối số khóa, thì chúng ta sắp xếp theo giá trị mà hàm khóa chỉ cho chúng ta.

Vì vậy, ví dụ sử dụng trả lại modulo là gì? Một giá trị boolean: True == 1, False == 0. Vậy làm thế nào để sắp xếp đối phó với chìa khóa này? Về cơ bản, nó biến đổi danh sách ban đầu thành một chuỗi 1 và 0.

[3,6,3,2,4,8,23] trở thành [0,1,0,1,1,1,0]

Bây giờ chúng tôi đang nhận được ở đâu đó. Bạn nhận được gì khi sắp xếp danh sách đã chuyển đổi?

[0,0,0,1,1,1,1]

Được rồi, vì vậy bây giờ chúng tôi biết tại sao tỷ lệ cược đến trước khi phát sinh. Nhưng câu hỏi tiếp theo là: Tại sao số 6 vẫn đến trước số 2 trong danh sách cuối cùng của tôi? Chà điều đó thật dễ dàng - bởi vì việc sắp xếp chỉ xảy ra một lần! tức là những số 1 đó vẫn đại diện cho các giá trị danh sách ban đầu, nằm ở vị trí ban đầu của chúng so với nhau. Vì việc sắp xếp chỉ xảy ra một lần và chúng tôi không gọi bất kỳ loại hàm sắp xếp nào để sắp xếp các giá trị chẵn gốc từ thấp đến cao, các giá trị đó vẫn theo thứ tự ban đầu so với nhau.

Câu hỏi cuối cùng là đây: Làm thế nào để tôi suy nghĩ một cách khái niệm về cách thứ tự các giá trị boolean của tôi được chuyển đổi trở lại các giá trị ban đầu khi tôi in ra danh sách được sắp xếp cuối cùng?

Sắp xếp () là một phương thức tích hợp sẵn (thực tế thú vị) sử dụng thuật toán sắp xếp lai được gọi là Timsortkết hợp các khía cạnh của sắp xếp hợp nhất và sắp xếp chèn. Đối với tôi có vẻ rõ ràng rằng khi bạn gọi nó, có một thợ máy giữ các giá trị này trong bộ nhớ và gói chúng với danh tính boolean (mặt nạ) được xác định bởi (...!) Hàm lambda. Thứ tự được xác định bởi danh tính boolean của chúng được tính từ hàm lambda, nhưng hãy nhớ rằng các danh sách con này (của một và số không) không được tự sắp xếp theo các giá trị ban đầu của chúng. Do đó, danh sách cuối cùng, trong khi được sắp xếp bởi Odds và Evens, không được sắp xếp theo danh sách phụ (các evens trong trường hợp này không theo thứ tự). Thực tế là tỷ lệ cược được đặt hàng là do chúng đã được sắp xếp theo thứ tự ngẫu nhiên trong danh sách ban đầu. Điểm nổi bật của tất cả những điều này là khi lambda thực hiện chuyển đổi đó, thứ tự ban đầu của danh sách con được giữ lại.

Vậy làm thế nào để tất cả những điều này liên quan trở lại câu hỏi ban đầu, và quan trọng hơn, trực giác của chúng ta về cách chúng ta nên thực hiện sắp xếp () với đối số chính và lambda của nó?

Hàm lambda đó có thể được coi là một con trỏ trỏ đến các giá trị mà chúng ta cần sắp xếp, cho dù con trỏ của nó ánh xạ một giá trị đến boolean của nó được biến đổi bởi hàm lambda hay nếu một phần tử cụ thể của nó trong danh sách lồng nhau, tuple, dict, vv, một lần nữa được xác định bởi chức năng lambda.

Hãy thử và dự đoán những gì sẽ xảy ra khi tôi chạy đoạn mã sau.

mylist = [(3, 5, 8), (6, 2, 8), ( 2, 9, 4), (6, 8, 5)]
sorted(mylist, key=lambda x: x[1])

Cuộc sortedgọi của tôi rõ ràng nói, "Hãy sắp xếp danh sách này". Đối số khóa làm cho cụ thể hơn một chút bằng cách nói, đối với mỗi phần tử (x) trong danh sách của tôi , trả về chỉ số 1 của phần tử đó, sau đó sắp xếp tất cả các phần tử của danh sách gốc 'mylist' theo thứ tự được sắp xếp của danh sách được tính theo hàm lambda. Vì chúng tôi có một danh sách các bộ dữ liệu, chúng tôi có thể trả về một phần tử được lập chỉ mục từ bộ dữ liệu đó. Vì vậy, chúng tôi nhận được:

[(6, 2, 8), (3, 5, 8), (6, 8, 5), (2, 9, 4)]

Chạy mã đó và bạn sẽ thấy rằng đây là thứ tự. Hãy thử lập chỉ mục một danh sách các số nguyên và bạn sẽ thấy rằng mã bị hỏng.

Đây là một lời giải thích dài dòng, nhưng tôi hy vọng điều này sẽ giúp 'sắp xếp' trực giác của bạn về việc sử dụng các hàm lambda làm đối số chính trong sort () và hơn thế nữa.


8
giải thích tuyệt vời và toàn diện. Câu trả lời này xứng đáng 100 điểm. Nhưng tôi tự hỏi tại sao thích ít hơn cho câu trả lời này.
Javed

3
Cảm ơn bạn đã giải thích sâu sắc. Tôi đọc các tài liệu và sắp xếp cách làm và tôi không rõ ý tưởng đằng sau keychức năng này. Nếu bạn đang cố gắng hiểu sortedchức năng, thì lambdacú pháp sẽ đi vào cách hiểu.
sanbor

3
Đây là lời giải thích tốt nhất ở đây. Điều này thực sự hữu ích trong việc giúp tôi hiểu cách thức này thực sự hoạt động - Tôi đã nắm bắt được hàm lambda, nhưng sử dụng nó trong ngữ cảnh được sắp xếp () không có ý nghĩa gì. Điều này thực sự có ích, cảm ơn!
TGWaffles

2
Đây là một câu trả lời tuyệt vời. Chào anh.
Rajesh Mappu

2
Đây là một trong những câu trả lời yêu thích của tôi về stack overflow. Cảm ơn bạn!
AdR

26

lambdalà một từ khóa Python được sử dụng để tạo các hàm ẩn danh .

>>> (lambda x: x+2)(3)
5

2
Tại sao có dấu ngoặc đơn xung quanh mỗi?
Christopher Markieta

19
Các parens ở xung quanh 3vì nó được chuyển đến một chức năng. Các parens nằm xung quanh lambda sao cho biểu thức không được phân tích cú pháp lambda x: x+2(3)là không hợp lệ vì 2không phải là hàm.
Ignacio Vazquez-Abrams

Tôi không chắc chắn tôi thích thuật ngữ "ẩn danh" chức năng. Ý tôi là, đúng là họ không được đặt tên, nên ẩn danh là "chính xác" về mặt kỹ thuật. Tôi muốn gọi chúng là "các chức năng tạm thời." Nhưng sau đó, tôi là một giáo viên.
dùng5179531

12

Bên variabletrái của :là một tên tham số. Việc sử dụng variablebên phải là sử dụng tham số.

Có nghĩa gần như chính xác như:

def some_method(variable):
  return variable[0]

5

Thêm một ví dụ về hàm sử dụng sort () với key = lambda. Hãy xem xét bạn có một danh sách các bộ dữ liệu. Trong mỗi bộ, bạn có một nhãn hiệu, mẫu mã và trọng lượng của xe và bạn muốn sắp xếp danh sách các bộ dữ liệu này theo nhãn hiệu, mẫu mã hoặc trọng lượng. Bạn có thể làm điều đó với lambda.

cars = [('citroen', 'xsara', 1100), ('lincoln', 'navigator', 2000), ('bmw', 'x5', 1700)]

print(sorted(cars, key=lambda car: car[0]))
print(sorted(cars, key=lambda car: car[1]))
print(sorted(cars, key=lambda car: car[2]))

Các kết quả:

[('bmw', 'x5', '1700'), ('citroen', 'xsara', 1100), ('lincoln', 'navigator', 2000)]
[('lincoln', 'navigator', 2000), ('bmw', 'x5', '1700'), ('citroen', 'xsara', 1100)]
[('citroen', 'xsara', 1100), ('bmw', 'x5', 1700), ('lincoln', 'navigator', 2000)]

3

lambdalà một hàm ẩn danh, không phải là một hàm tùy ý. Tham số được chấp nhận sẽ là biến bạn đang làm việc và cột mà bạn đang sắp xếp nó.



1

Chỉ cần viết lại, khóa (Tùy chọn. Hàm để thực thi để quyết định thứ tự. Mặc định là Không có) trong các hàm được sắp xếp mong đợi một hàm và bạn sử dụng lambda.

Để xác định lambda, bạn chỉ định thuộc tính đối tượng bạn muốn sắp xếp và hàm được sắp xếp sẵn của python sẽ tự động chăm sóc nó.

Nếu bạn muốn sắp xếp theo nhiều thuộc tính thì gán key = lambda x: (property1, property2).

Để chỉ định thứ tự, chuyển ngược lại = true làm đối số thứ ba (Tùy chọn. Boolean. Sai sẽ sắp xếp tăng dần, True sẽ sắp xếp giảm dần. Mặc định là Sai) của hàm được sắp xếp.


1

Câu trả lời đơn giản và không tốn thời gian với một ví dụ liên quan đến câu hỏi được hỏi Thực hiện theo ví dụ này:

 user = [{"name": "Dough", "age": 55}, 
            {"name": "Ben", "age": 44}, 
            {"name": "Citrus", "age": 33},
            {"name": "Abdullah", "age":22},
            ]
    print(sorted(user, key=lambda el: el["name"]))
    print(sorted(user, key= lambda y: y["age"]))

Nhìn vào tên trong danh sách, chúng bắt đầu bằng D, B, C và A. Và nếu bạn chú ý độ tuổi, chúng là 55, 44, 33 và 22. Mã in đầu tiên

print(sorted(user, key=lambda el: el["name"]))

Kết quả cho:

[{'name': 'Abdullah', 'age': 22}, 
{'name': 'Ben', 'age': 44}, 
{'name': 'Citrus', 'age': 33}, 
{'name': 'Dough', 'age': 55}]

sắp xếp tên, bởi vì theo key = lambda el: el ["name"] chúng tôi đang sắp xếp tên và tên trả về theo thứ tự bảng chữ cái.

Mã in thứ hai

print(sorted(user, key= lambda y: y["age"]))

Kết quả:

[{'name': 'Abdullah', 'age': 22},
 {'name': 'Citrus', 'age': 33},
 {'name': 'Ben', 'age': 44}, 
 {'name': 'Dough', 'age': 55}]

sắp xếp theo độ tuổi và do đó, danh sách trả về theo thứ tự tăng dần theo độ tuổi.

Hãy thử mã này để hiểu rõ hơn.

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.