คำถามนี้สัมผัสส่วนที่มีกลิ่นเหม็นมากของ "Python" ที่มีชื่อเสียง "และ" ชัดเจน "- สิ่งที่จะมาก่อน, แลมบ์ดาหรือสำหรับความเข้าใจในรายการ
ฉันไม่คิดว่าวัตถุประสงค์ของ OP คือการสร้างรายการของช่องสี่เหลี่ยมจาก 0 ถึง 9 หากเป็นกรณีนี้เราสามารถให้คำตอบได้มากกว่าเดิม:
squares = []
for x in range(10): squares.append(x*x)
- นี่เป็นวิธีการไวยากรณ์ที่ดีของเฒ่า
แต่มันไม่ใช่ประเด็น ประเด็นคือ W (hy) TF คือการแสดงออกที่คลุมเครือดังนั้นตอบโต้ได้ง่าย? และฉันมีคดีงี่เง่าสำหรับคุณในตอนท้ายดังนั้นอย่าเพิกเฉยคำตอบของฉันเร็วเกินไป (ฉันมีมันในการสัมภาษณ์งาน)
ดังนั้นความเข้าใจของ OP จึงส่งกลับรายการ lambdas:
[(lambda x: x*x) for x in range(10)]
นี่เป็นเพียง 10 สำเนาที่แตกต่างกันของฟังก์ชันกำลังสองดู:
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
หมายเหตุที่อยู่หน่วยความจำของ lambdas - พวกมันต่างกัน!
แน่นอนว่าคุณสามารถมีการแสดงออกนี้ "ดีที่สุด" (ฮ่าฮ่า) รุ่นอื่น ๆ :
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
ดู? 3 ครั้งแลมบ์ดาเดียวกัน
โปรดทราบว่าฉันใช้_เป็นforตัวแปร มันไม่มีส่วนเกี่ยวข้องกับสิ่งที่xอยู่ในlambda(มันถูกบดบังด้วยคำศัพท์!) รับมัน
ฉันออกจากการสนทนาทำไมความสำคัญของไวยากรณ์ไม่เป็นเช่นนั้นมันหมายถึงทั้งหมด:
[lambda x: (x*x for x in range(10))]
ซึ่งอาจเป็น: [[0, 1, 4, ..., 81]]หรือหรือ[(0, 1, 4, ..., 81)]หรือฉันพบว่ามีเหตุผลมากที่สุดนี่จะเป็นหนึ่งlistในองค์ประกอบ - การgeneratorคืนค่า มันไม่ใช่กรณีภาษานั้นใช้งานไม่ได้
แต่ถ้าเป็น ...
ถ้าคุณไม่บดบังforตัวแปรและใช้มันในlambdas?
อึนั้นก็เกิดขึ้น ดูนี่สิ:
[lambda x: x * i for i in range(4)]
แน่นอนว่าวิธีนี้:
[(lambda x: x * i) for i in range(4)]
แต่มันไม่ได้หมายความว่า:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
นี่มันบ้าไปแล้ว!
lambdas ในรายการความเข้าใจเป็นการปิดขอบเขตของความเข้าใจนี้ การปิดคำศัพท์ดังนั้นพวกเขาจึงอ้างถึงการiอ้างอิงผ่านไม่ใช่ค่าของมันเมื่อพวกเขาถูกประเมิน!
ดังนั้นการแสดงออกนี้:
[(lambda x: x * i) for i in range(4)]
ใกล้เคียงกับ:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
ฉันแน่ใจว่าเราสามารถดูเพิ่มเติมได้ที่นี่โดยใช้disไพ ธ อลเดอร์ไพ ธ อน(ซึ่งฉันหมายถึงเช่นโมดูล) แต่สำหรับการสนทนา Python-VM-agnostic ไม่เพียงพอนี่ก็เพียงพอแล้ว มากสำหรับคำถามสัมภาษณ์งาน
ทีนี้วิธีทำlistlambdas ทวีคูณซึ่งคูณด้วยจำนวนเต็มต่อเนื่องจริง ๆ ? เช่นเดียวกับคำตอบที่ยอมรับเราต้องแบ่งการผูกโดยตรงiโดยการพันคำอีกคำหนึ่งlambdaซึ่งถูกเรียกในนิพจน์ความเข้าใจรายการ:
ก่อน:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
หลังจาก:
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(ฉันมีตัวแปรแลมบ์ดาด้านนอกด้วย = iแต่ฉันคิดว่านี่เป็นวิธีแก้ปัญหาที่ชัดเจนกว่า - ฉันแนะนำyเพื่อให้เราทุกคนเห็นว่าแม่มดคนไหนเป็นคนไหน)
แก้ไข 2019-08-30:
ทำตามคำแนะนำโดย @ josoler ซึ่งมีอยู่ในคำตอบโดย @sheridp ด้วย - ค่าของความเข้าใจในรายการ "ตัวแปรวนรอบ" สามารถเป็น "ฝังตัว" ภายในวัตถุได้ - กุญแจสำหรับให้เข้าถึงในเวลาที่เหมาะสม ส่วน "หลังจาก" ดังกล่าวข้างต้นไม่ได้โดยการตัดมันอีกและเรียกมันว่าทันทีที่มีมูลค่าปัจจุบันของlambda iอีกวิธีหนึ่ง (อ่านง่ายขึ้นนิดหน่อย - มันไม่มีผล 'WAT') คือการเก็บค่าของวัตถุiไว้ภายในpartialและให้ "inner" (ดั้งเดิม) lambdaถือเป็นอาร์กิวเมนต์ (ส่งผ่านโดยpartialวัตถุที่ เวลาที่โทร) คือ:
หลังจาก 2:
>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
เยี่ยมมาก แต่ก็ยังมีเรื่องเล็กน้อยสำหรับคุณ! สมมติว่าเราไม่ต้องการทำให้เครื่องอ่านโค้ดง่ายขึ้นและส่งปัจจัยตามชื่อ (เป็นอาร์กิวเมนต์ของคำหลักpartial) มาทำการเปลี่ยนชื่อกันบ้าง:
หลังจาก 2.5:
>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'
วัด?
>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'
เดี๋ยวก่อน ... เรากำลังเปลี่ยนจำนวนอาร์กิวเมนต์เป็น 1 และเปลี่ยนจาก "มากเกินไป" เป็น "น้อยเกินไป" หรือไม่
ดีก็ไม่ได้เป็นวัดจริงเมื่อเราผ่านcoefไปpartialในลักษณะนี้มันจะกลายเป็นข้อโต้แย้งคำหลักดังนั้นจึงต้องมาหลังจากที่ตำแหน่งxโต้แย้งเช่นดังนั้น:
หลังจาก 3:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
ฉันต้องการเวอร์ชั่นล่าสุดมากกว่าแลมบ์ดาที่ซ้อนกัน แต่สำหรับแต่ละคน ...
[lambda x: x*x for x in range(10)]เร็วกว่าอันแรกเนื่องจากไม่เรียกใช้ฟังก์ชันลูปนอก f ซ้ำ ๆ