ไวยากรณ์หลังเรียงแล้ว (key = lambda: …)


152

ฉันไม่เข้าใจไวยากรณ์หลังsorted()อาร์กิวเมนต์:

key=lambda variable: variable[0]

มันไม่ได้lambdaตามอำเภอใจเหรอ? ทำไมvariableที่ระบุไว้สองครั้งในสิ่งที่ดูเหมือนว่าdict?

คำตอบ:


162

keyเป็นฟังก์ชั่นที่จะถูกเรียกให้แปลงรายการของคอลเลกชันก่อนที่จะถูกเปรียบเทียบ พารามิเตอร์ที่ส่งผ่านไปkeyจะต้องเป็นสิ่งที่เรียกได้

การใช้lambdaสร้างฟังก์ชั่นที่ไม่ระบุชื่อ (ซึ่งเรียกได้) ในกรณีsortedที่ callable ใช้เพียงหนึ่งพารามิเตอร์ Python lambdaนั้นค่อนข้างเรียบง่าย มันสามารถทำได้และส่งคืนสิ่งหนึ่งเท่านั้นจริงๆ

ไวยากรณ์ของlambdaคือคำที่lambdaตามด้วยรายการชื่อพารามิเตอร์จากนั้นบล็อกหนึ่งรหัส รายการพารามิเตอร์และการบล็อกรหัสถูกกำหนดโดยโคลอน นี้จะคล้ายกับโครงสร้างอื่น ๆ ในหลามเช่นกันเช่นwhile, for, ifและอื่น ๆ พวกเขาเป็นงบทั้งหมดที่มักจะมีการบล็อกรหัส แลมบ์ดาเป็นอีกตัวอย่างหนึ่งของคำสั่งที่มีการบล็อกรหัส

เราสามารถเปรียบเทียบการใช้แลมบ์ดากับของ def เพื่อสร้างฟังก์ชั่น

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

แลมบ์ดาให้วิธีในการทำสิ่งนี้กับเราโดยไม่ต้องระบุชื่อ ซึ่งทำให้ยอดเยี่ยมสำหรับการใช้เป็นพารามิเตอร์ของฟังก์ชัน

variable มีการใช้สองครั้งที่นี่เพราะทางด้านซ้ายของเครื่องหมายโคลอนจะเป็นชื่อของพารามิเตอร์และทางด้านขวามือจะใช้ในบล็อกโค้ดเพื่อคำนวณบางอย่าง


10
หมายเหตุ (ถึง OP): ควรหลีกเลี่ยงการกำหนดแลมบ์ดาให้กับชื่อ (เช่นใช้ในวิธีอื่นที่ไม่ใช่ฟังก์ชั่นที่ไม่ระบุชื่อ) defถ้าคุณพบว่าตัวเองทำเช่นนี้คุณอาจจะเพียงแค่ใช้
Wim

151

ฉันคิดว่าคำตอบทั้งหมดที่นี่ครอบคลุมหลักของสิ่งที่ฟังก์ชั่นแลมบ์ดาในบริบทของการเรียงลำดับ () ค่อนข้างดี แต่ฉันยังรู้สึกเหมือนคำอธิบายที่นำไปสู่ความเข้าใจที่เข้าใจง่ายขาดนี่คือสองเซ็นต์ของฉัน

เพื่อประโยชน์ของความสมบูรณ์ฉันจะแจ้งให้ทราบล่วงหน้า: sort () ส่งกลับรายการขององค์ประกอบที่เรียงและถ้าเราต้องการเรียงลำดับในลักษณะเฉพาะหรือถ้าเราต้องการเรียงลำดับรายการที่ซับซ้อนขององค์ประกอบ (เช่นรายการซ้อนหรือ รายการของ tuples) เราสามารถเรียกใช้อาร์กิวเมนต์ที่สำคัญ

สำหรับฉันความเข้าใจอย่างถ่องแท้เกี่ยวกับข้อโต้แย้งที่สำคัญว่าทำไมจึงต้องมี callable และการใช้แลมบ์ดาเป็นฟังก์ชั่น callable (ไม่ระบุชื่อ) เพื่อให้บรรลุสิ่งนี้มาในสองส่วน

  1. ท้ายที่สุดการใช้ lamba หมายความว่าคุณไม่จำเป็นต้องเขียน (define) ฟังก์ชั่นทั้งหมดเช่นเดียวกับsblom ที่ให้ตัวอย่าง ฟังก์ชั่นของแลมบ์ดานั้นถูกสร้างใช้และทำลายในทันทีดังนั้นพวกเขาจึงไม่ทำให้โค้ดของคุณแย่ลงด้วยรหัสมากขึ้นซึ่งจะถูกใช้เพียงครั้งเดียว อย่างที่ฉันเข้าใจแล้วนี่คือยูทิลิตี้หลักของฟังก์ชั่นแลมบ์ดาและแอพพลิเคชั่นของมันสำหรับบทบาทดังกล่าวนั้นกว้าง ไวยากรณ์ของมันคือการประชุมอย่างหมดจดซึ่งเป็นสาระสำคัญธรรมชาติของไวยากรณ์โปรแกรมโดยทั่วไป เรียนรู้ไวยากรณ์และดำเนินการกับมัน

ไวยากรณ์ของแลมบ์ดาเป็นดังนี้:

แลมบ์ดาinput_variable (s) : อร่อยหนึ่งซับ

เช่น

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. แนวคิดเบื้องหลังการkeyโต้แย้งคือควรใช้ชุดคำสั่งที่จะชี้ฟังก์ชัน 'sort ()' ที่องค์ประกอบรายการเหล่านั้นซึ่งควรใช้ในการเรียงลำดับ เมื่อมันบอกkey=ว่ามันหมายถึงอะไรจริงๆ: เมื่อฉันวนซ้ำองค์ประกอบหนึ่งรายการในแต่ละครั้ง (เช่นสำหรับ e ในรายการ) ฉันจะส่งองค์ประกอบปัจจุบันไปยังฟังก์ชันที่ฉันให้ไว้ในอาร์กิวเมนต์ที่สำคัญและใช้สิ่งนั้น เพื่อสร้างรายการที่แปลงแล้วซึ่งจะแจ้งให้ฉันทราบเกี่ยวกับลำดับของรายการที่เรียงลำดับสุดท้าย

ลองดูสิ:

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

ตัวอย่างฐาน:

sorted(mylist)

[2, 3, 3, 4, 6, 8, 23] # ตัวเลขทั้งหมดเรียงตามลำดับจากเล็กไปใหญ่

ตัวอย่างที่ 1:

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

[3, 3, 23, 6, 2, 4, 8] # ผลลัพธ์ที่เรียงลำดับนี้เหมาะสมกับคุณหรือไม่

โปรดสังเกตว่าฟังก์ชั่นแลมบ์ดาของฉันบอกว่าเรียงลำดับเพื่อตรวจสอบว่า (e) เป็นเลขคู่หรือคี่ก่อนที่จะเรียงลำดับ

แต่เดี๋ยวก่อน! คุณอาจสงสัย (หรือควรจะ) สองสิ่ง - ก่อนทำไมราคาของฉันมาก่อนเหตุการณ์ของฉัน (เนื่องจากค่าคีย์ของฉันดูเหมือนจะบอกฟังก์ชั่นการเรียงลำดับของฉันเพื่อจัดลำดับความสำคัญของเหตุการณ์โดยใช้ตัวดำเนินการ mod x%2==0) ประการที่สองทำไมฉันถึงไม่เป็นระเบียบ? 2 มาก่อน 6 ใช่ไหม? โดยการวิเคราะห์ผลลัพธ์นี้เราจะได้เรียนรู้สิ่งที่ลึกซึ้งยิ่งขึ้นเกี่ยวกับวิธีการเรียงลำดับ () คีย์ 'อาร์กิวเมนต์' โดยเฉพาะอย่างยิ่งเมื่อใช้ร่วมกับฟังก์ชันแลมบ์ดานิรนาม

ประการแรกคุณจะสังเกตเห็นว่าในขณะที่อัตราต่อรองมาก่อนที่ Evens, Evens ตัวเองไม่ได้เรียง ทำไมนี้ ให้อ่านเอกสาร :

ฟังก์ชั่นคีย์เริ่มต้นด้วย Python 2.4 ทั้ง list.sort () และเรียง () เพิ่มพารามิเตอร์คีย์เพื่อระบุฟังก์ชั่นที่จะเรียกใช้ในแต่ละรายการก่อนที่จะทำการเปรียบเทียบ

เราต้องอ่านนิดหน่อยระหว่างบรรทัดที่นี่ แต่สิ่งที่บอกเราคือฟังก์ชั่นการเรียงลำดับจะถูกเรียกเพียงครั้งเดียวและถ้าเราระบุอาร์กิวเมนต์หลักเราจะเรียงตามค่าที่ฟังก์ชันหลักชี้ไป

ตัวอย่างการใช้โมดูโล่ส่งคืนอะไร ค่าบูลีน: True == 1, False == 0. ดังนั้นการเรียงลำดับจะจัดการกับคีย์นี้อย่างไร โดยทั่วไปจะแปลงรายการดั้งเดิมเป็นลำดับ 1 และ 0

[3,6,3,2,4,8,23] กลายเป็น [0,1,0,1,1,1,1,0]

ตอนนี้เรากำลังเดินทางอยู่ที่ไหนซักแห่ง คุณจะได้อะไรเมื่อคุณเรียงลำดับรายการที่ถูกแปลง?

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

ตกลงดังนั้นตอนนี้เรารู้ว่าทำไมอัตราต่อรองมาก่อนเหตุการณ์ แต่คำถามต่อไปคือ: ทำไม 6 ถึงยังมาก่อน 2 ในรายการสุดท้ายของฉัน ง่ายมาก - เพราะการเรียงลำดับเกิดขึ้นเพียงครั้งเดียว! นั่นคือ 1s เหล่านั้นยังคงเป็นตัวแทนของค่ารายการดั้งเดิมซึ่งอยู่ในตำแหน่งดั้งเดิมที่สัมพันธ์กัน เนื่องจากการเรียงลำดับจะเกิดขึ้นเพียงครั้งเดียวและเราไม่เรียกฟังก์ชันเรียงลำดับใด ๆ เพื่อเรียงลำดับค่าดั้งเดิมจากต่ำไปสูงค่าเหล่านั้นยังคงอยู่ในลำดับเดิมที่สัมพันธ์กัน

คำถามสุดท้ายคือคำถามนี้ฉันจะคิดอย่างไรเกี่ยวกับแนวคิดว่าค่าบูลีนของฉันจะเปลี่ยนกลับไปเป็นค่าดั้งเดิมได้อย่างไรเมื่อฉันพิมพ์รายการที่เรียงลำดับสุดท้าย

Sorted () เป็นวิธีการในตัวที่ (สนุกจริง) ใช้อัลกอริทึมการจัดเรียงไฮบริดที่เรียกว่าTimsortที่รวมแง่มุมของการผสานการเรียงลำดับและการเรียงลำดับการแทรก เห็นได้ชัดว่าเมื่อคุณเรียกมันว่ามีกลไกที่เก็บค่าเหล่านี้ไว้ในหน่วยความจำและรวมเข้ากับตัวตนบูลีน (มาสก์) ซึ่งกำหนดโดย (... !) ฟังก์ชั่นแลมบ์ดา คำสั่งจะถูกกำหนดโดยตัวตนบูลีนที่คำนวณจากฟังก์ชันแลมบ์ดา แต่โปรดจำไว้ว่ารายการย่อยเหล่านี้ (ของรายการและศูนย์) จะไม่เรียงลำดับตามค่าดั้งเดิม ดังนั้นรายการสุดท้ายในขณะที่จัดระเบียบโดย Odds และ Evens จะไม่เรียงลำดับตามรายการย่อย (Evens ในกรณีนี้ไม่เป็นระเบียบ) ความจริงที่ว่าอัตราต่อรองที่ถูกสั่งซื้อเป็นเพราะพวกเขาอยู่ในลำดับโดยบังเอิญในรายการเดิม สิ่งที่นำมาจากทั้งหมดนี้คือเมื่อแลมบ์ดาทำการเปลี่ยนแปลงนั้นลำดับเดิมของรายการย่อยจะถูกเก็บไว้

แล้วทั้งหมดนี้เกี่ยวข้องกับคำถามเดิมอย่างไรและที่สำคัญกว่านั้นคือสัญชาตญาณของเราว่าเราควรใช้การเรียงลำดับ () ด้วยอาร์กิวเมนต์หลักและแลมบ์ดาอย่างไร

ฟังก์ชั่นแลมบ์ดานั้นคิดว่าเป็นตัวชี้ที่ชี้ไปยังค่าที่เราต้องการเรียงลำดับไม่ว่าจะเป็นตัวชี้การแมปค่ากับบูลีนที่แปลงโดยฟังก์ชันแลมบ์ดาหรือถ้าองค์ประกอบเฉพาะในรายการซ้อน dict ฯลฯ ถูกกำหนดอีกครั้งโดยฟังก์ชั่นแลมบ์ดา

ลองและทำนายว่าจะเกิดอะไรขึ้นเมื่อฉันเรียกใช้รหัสต่อไปนี้

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

sortedเห็นได้ชัดว่าสายของฉันพูดว่า "โปรดเรียงลำดับรายการนี้" อาร์กิวเมนต์ key ทำให้มีความเฉพาะเจาะจงมากขึ้นโดยการพูดสำหรับแต่ละองค์ประกอบ (x) ใน mylist ให้ส่งคืนดัชนี1ขององค์ประกอบนั้นแล้วเรียงลำดับองค์ประกอบทั้งหมดของรายการเดิม 'mylist' ตามลำดับการเรียงลำดับของรายการที่คำนวณโดย ฟังก์ชั่นแลมบ์ดา เนื่องจากเรามีรายการสิ่งอันดับเราสามารถส่งคืนองค์ประกอบที่จัดทำดัชนีจากสิ่งอันดับ ดังนั้นเราจึงได้รับ:

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

เรียกใช้รหัสนั้นและคุณจะพบว่านี่คือคำสั่ง ลองสร้างดัชนีรายการจำนวนเต็มและคุณจะพบว่าตัวแบ่งรหัส

นี่เป็นคำอธิบายที่ยืดยาว แต่ฉันหวังว่านี่จะช่วย 'จัดเรียง' สัญชาตญาณของคุณเกี่ยวกับการใช้ฟังก์ชั่นแลมบ์ดาเป็นอาร์กิวเมนต์หลักในการเรียงลำดับ () และอื่น ๆ


8
คำอธิบายที่ยอดเยี่ยมและครอบคลุม คำตอบนี้สมควรได้รับ 100 คะแนน แต่ฉันสงสัยว่าทำไมคำตอบนี้ถึงถูกใจน้อยกว่า
javed

3
ขอบคุณสำหรับคำอธิบายที่ลึกซึ้ง ฉันอ่านเอกสารและการเรียงลำดับวิธีการและมันไม่ชัดเจนสำหรับฉันแนวคิดเบื้องหลังkeyฟังก์ชั่น หากคุณพยายามที่จะเข้าใจsortedฟังก์ชั่นlambdaไวยากรณ์เหล่านั้นจะเข้าสู่ความเข้าใจ
sanbor

3
นี่คือคำอธิบายที่ดีที่สุดที่นี่ นี่มีประโยชน์มากในการช่วยให้ฉันเข้าใจว่ามันใช้งานได้จริง - ฉัน sorta เข้าใจฟังก์ชั่นแลมบ์ดา แต่การใช้มันในบริบทของการเรียง () ไม่สมเหตุสมผล สิ่งนี้ช่วยได้จริงๆขอบคุณ!
TGWaffles

2
นี่คือคำตอบที่ยอดเยี่ยม แสดงความยินดีกับคุณครับ
Rajesh Mappu

2
นี่เป็นหนึ่งในคำตอบที่ฉันโปรดปรานในการล้นสแต็ค ขอบคุณ!
ADR

26

lambdaเป็นคำหลักงูใหญ่ที่ใช้ในการสร้างฟังก์ชั่นที่ไม่ระบุชื่อ

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

2
เหตุใดจึงมีวงเล็บแต่ละรอบ
Christopher Markieta

19
parens อยู่ใกล้3เพราะมันถูกส่งผ่านไปยังฟังก์ชั่น parens อยู่รอบแลมบ์ดาเพื่อไม่ให้มีการแยกวิเคราะห์นิพจน์lambda x: x+2(3)ซึ่งไม่ถูกต้องเนื่องจาก2ไม่ใช่ฟังก์ชัน
Ignacio Vazquez-Abrams

ฉันไม่แน่ใจว่าฉันชอบคำว่า "ไม่ระบุชื่อ" ฟังก์ชั่น ฉันหมายความว่ามันเป็นความจริงที่ว่าพวกเขาไม่ได้ตั้งชื่อ ฉันอยากจะเรียกพวกเขาว่า "ฟังก์ชั่นชั่วคราว" แต่ฉันเป็นคนอวดรู้
user5179531

12

variableซ้ายของ:เป็นชื่อพารามิเตอร์ การใช้งานของvariableด้านขวาคือการใช้พารามิเตอร์

หมายถึงเกือบเหมือนกับ:

def some_method(variable):
  return variable[0]

5

อีกหนึ่งตัวอย่างของการใช้งาน sort ()ฟังก์ชั่นที่มี key = lambda สมมติว่าคุณมีรายการสิ่งอันดับ ใน tuple แต่ละอันคุณมียี่ห้อรุ่นและน้ำหนักของรถและคุณต้องการเรียงลำดับรายการของ tuples ตามยี่ห้อรุ่นหรือน้ำหนัก คุณสามารถทำได้ด้วยแลมบ์ดา

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]))

ผล:

[('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

lambdaเป็นฟังก์ชั่นนิรนามไม่ใช่ฟังก์ชั่นโดยพลการ พารามิเตอร์ที่ได้รับการยอมรับจะเป็นตัวแปรที่คุณใช้งานและคอลัมน์ที่คุณกำลังเรียงลำดับ



1

เพียงเพื่อใช้ถ้อยคำใหม่กุญแจ (เป็นทางเลือกฟังก์ชั่นเพื่อดำเนินการเพื่อตัดสินใจสั่งซื้อค่าเริ่มต้นคือไม่มี) ในฟังก์ชั่นที่เรียงลำดับคาดว่าจะมีฟังก์ชั่นและคุณใช้แลมบ์ดา

ในการกำหนดแลมบ์ดาคุณระบุคุณสมบัติของวัตถุที่คุณต้องการเรียงลำดับและฟังก์ชั่นการเรียงลำดับในตัวของงูใหญ่จะดูแลโดยอัตโนมัติ

หากคุณต้องการเรียงลำดับตามคุณสมบัติหลายรายการให้กำหนด key = lambda x: (property1, property2)

ในการระบุคำสั่งซื้อโดยส่งผ่าน reverse = true เป็นอาร์กิวเมนต์ที่สาม (เป็นทางเลือกบูลีน False จะเรียงลำดับจากน้อยไปมาก True จะเรียงจากมากไปน้อยค่าเริ่มต้นคือเท็จ) ของฟังก์ชันที่เรียงลำดับ


1

คำตอบที่ง่ายและไม่เสียเวลากับตัวอย่างที่เกี่ยวข้องกับคำถามที่ถาม ทำตามตัวอย่างนี้:

 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"]))

ดูชื่อในรายการพวกเขาเริ่มต้นด้วย D, B, C และ A และถ้าคุณสังเกตเห็นอายุพวกเขาคือ 55, 44, 33 และ 22 รหัสการพิมพ์ครั้งแรก

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

ผลลัพธ์ไปที่:

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

เรียงลำดับชื่อเพราะโดย key = lambda el: el ["name"] เรากำลังเรียงลำดับชื่อและชื่อส่งกลับตามลำดับตัวอักษร

รหัสการพิมพ์ที่สอง

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

ผลลัพธ์:

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

เรียงตามอายุและด้วยเหตุนี้รายการจึงส่งกลับตามลำดับอายุ

ลองใช้รหัสนี้เพื่อความเข้าใจที่ดีขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.