ควรใช้แบบใดมากกว่ากัน: ฟังก์ชันแลมบ์ดาหรือฟังก์ชันซ้อนกัน ('def')


104

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

ต่อไปนี้เป็นตัวอย่างเล็กน้อยที่พวกเขาทำหน้าที่เหมือนกันหากพบในฟังก์ชันอื่น:

ฟังก์ชัน Lambda

>>> a = lambda x : 1 + x
>>> a(5)
6

ฟังก์ชันซ้อนกัน

>>> def b(x): return 1 + x

>>> b(5)
6

มีข้อดีในการใช้อย่างอื่นหรือไม่? (ประสิทธิภาพการอ่านข้อ จำกัด ความสม่ำเสมอ ฯลฯ )

มันสำคัญหรือไม่? หากไม่เป็นเช่นนั้นจะเป็นการละเมิดหลักการของ Pythonic:

ควรจะมีหนึ่งและโดยเฉพาะอย่างยิ่งเพียงหนึ่งที่เห็นได้ชัดวิธีที่จะทำมัน

คำตอบ:


108

หากคุณต้องการกำหนดให้lambdaกับชื่อให้ใช้ a defแทน defs เป็นเพียงน้ำตาลที่ใช้ในการกำหนดงานดังนั้นผลลัพธ์จึงเหมือนกันและมีความยืดหยุ่นและอ่านง่ายกว่ามาก

lambdas สามารถใช้งานได้ครั้งเดียวทิ้งฟังก์ชันที่ไม่มีชื่อ

อย่างไรก็ตามกรณีการใช้งานนี้หายากมาก คุณแทบไม่จำเป็นต้องผ่านวัตถุฟังก์ชันที่ไม่มีชื่อ

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

สำหรับกรณีที่คุณต้องการวัตถุฟังก์ชันขนาดเล็กคุณควรใช้operatorฟังก์ชันโมดูลเช่นoperator.addแทนที่จะเป็นlambda x, y: x + y

หากคุณยังต้องการบางส่วนที่lambdaไม่ครอบคลุมคุณอาจลองเขียน a defเพื่อให้อ่านง่ายขึ้น ถ้าฟังก์ชันซับซ้อนกว่าฟังก์ชันในoperatorโมดูล a defน่าจะดีกว่า

ดังนั้นlambdaกรณีการใช้งานที่ดีในโลกแห่งความเป็นจริงจึงหายากมาก


10
ฉันเห็นด้วยกับคำตอบว่าควรใช้เมื่อใดlambdaแต่ฉันไม่เห็นด้วยว่านี่เป็น "หายากมาก" ซึ่งเป็นเรื่องปกติสำหรับฟังก์ชันหลักsortedหรือitertools.groupbyอื่น ๆ เช่นsorted(['a1', 'b0'], key= lambda x: int(x[1]))
Chris_Rands

31

ในทางปฏิบัติสำหรับฉันมีความแตกต่างสองประการ:

อย่างแรกคือเกี่ยวกับสิ่งที่พวกเขาทำและสิ่งที่พวกเขากลับมา:

  • def เป็นคำหลักที่ไม่ส่งคืนอะไรเลยและสร้าง 'ชื่อ' ในเนมสเปซท้องถิ่น

  • แลมบ์ดาเป็นคีย์เวิร์ดที่ส่งคืนอ็อบเจ็กต์ฟังก์ชันและไม่สร้าง 'ชื่อ' ในเนมสเปซโลคัล

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

ในบางกรอบนี่เป็นเรื่องธรรมดา ตัวอย่างเช่นฉันใช้Twisted บ่อยมากและทำสิ่งที่ชอบ

d.addCallback(lambda result: setattr(self, _someVariable, result))

เป็นเรื่องธรรมดาและกระชับกับ lambdas

ข้อแตกต่างประการที่สองคือสิ่งที่ฟังก์ชันจริงได้รับอนุญาตให้ทำ

  • ฟังก์ชันที่กำหนดด้วย 'def' สามารถมีรหัสหลามได้
  • ฟังก์ชันที่กำหนดด้วย 'lambda' ต้องประเมินเป็นนิพจน์ดังนั้นจึงไม่สามารถมีคำสั่งเช่น print, import, Raise, ...

ตัวอย่างเช่น,

def p(x): print x

ทำงานได้ตามที่คาดไว้ในขณะที่

lambda x: print x

เป็น SyntaxError

แน่นอนว่ายังมีวิธีการแก้ปัญหา - แทนprintด้วยsys.stdout.writeหรือกับimport __import__แต่โดยปกติแล้วคุณควรใช้ฟังก์ชันนี้ดีกว่า


23

ในการสัมภาษณ์ครั้งนี้ Guido van Rossum กล่าวว่าเขาหวังว่าเขาจะไม่ปล่อยให้ 'lambda' เข้าไปใน Python:

" ถาม: คุณลักษณะใดของ Python ที่คุณพอใจน้อยที่สุด

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

ในทางปฏิบัติมันไม่ได้ดีขนาดนั้น Python มีเพียงสองขอบเขตเท่านั้น: ท้องถิ่นและทั่วโลก สิ่งนี้ทำให้การเขียนฟังก์ชันแลมบ์ดาเจ็บปวดเพราะคุณมักต้องการเข้าถึงตัวแปรในขอบเขตที่กำหนดแลมบ์ดา แต่คุณทำไม่ได้เนื่องจากสองขอบเขต มีวิธีแก้ปัญหานี้ แต่มันเป็นสิ่งที่น่าสนใจ บ่อยครั้งที่ Python ดูเหมือนง่ายกว่ามากที่จะใช้ for loop แทนที่จะไปยุ่งกับฟังก์ชัน lambda แผนที่และเพื่อนจะทำงานได้ดีก็ต่อเมื่อมีฟังก์ชันในตัวที่ทำสิ่งที่คุณต้องการอยู่แล้ว

IMHO, Iambdas อาจสะดวกในบางครั้ง แต่โดยปกติแล้วจะสะดวกด้วยค่าใช้จ่ายในการอ่าน คุณบอกฉันได้ไหมว่าสิ่งนี้ทำอย่างไร:

str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]

ฉันเขียนมันและฉันใช้เวลาหนึ่งนาทีในการคิดออก นี่มาจาก Project Euler - ฉันจะไม่บอกว่าปัญหาไหนเพราะฉันเกลียดสปอยเลอร์ แต่มันทำงานใน 0.124 วินาที :)


20
โปรดทราบว่าบทสัมภาษณ์ค่อนข้างเก่าและ Python ได้เพิ่มขอบเขตที่ซ้อนกันมานานแล้วซึ่งทำให้การโต้แย้งที่เขาให้กับแลมด้าไม่เกี่ยวข้องอีกต่อไป ฉันแน่ใจว่าเขายังคงเสียใจกับ lambda แต่ก็ไม่เพียงพอที่จะลบออกใน Python 3.0
Thomas Wouters

10
จริงๆตัวอย่างของคุณควรเป็นการโต้แย้งกับ one-liners ไม่ใช่ lambdas นอกจากนี้คุณควรใช้ฟังก์ชัน sum ในตัวแทนการลดด้วย lambda: str (sum (map (lambda x: x ** x, range (1001)))) [: - 10]
Triptych

2
@ThomasWouters: ฉันเข้าใจว่าการlambdaไม่ถูกลบออกใน 3.0 เป็นเรื่องใกล้ตัวและ Guido ก็ไม่ได้ต่อสู้เพื่อรักษามันไว้
Ethan Furman

11

สำหรับ n = 1000 นี่เป็นช่วงเวลาของการเรียกใช้ฟังก์ชันเทียบกับแลมด้า:

In [11]: def f(a, b):
             return a * b

In [12]: g = lambda x, y: x * y

In [13]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    f(a, b)
   ....:
100 loops, best of 3: 285 ms per loop

In [14]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    g(a, b)
   ....:
100 loops, best of 3: 298 ms per loop

In [15]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    (lambda x, y: x * y)(a, b)
   ....:
100 loops, best of 3: 462 ms per loop

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

ฉันเดาว่านี่สมเหตุสมผลเนื่องจากคำจำกัดความอาจอ้างอิงตัวแปรภายใน (ซึ่งอาจมีการเปลี่ยนแปลง) ... แม้ว่าในกรณีที่ไม่เหมือนที่นี่ cpython สามารถทำงานได้ดีกว่า
Andy Hayden

ใช้ dis.dis; (แลมบ์ดา x, y: x * y) ของคุณสร้างฟังก์ชันทุกๆลูป หากคุณสร้างแลมด้าก่อนลูป (aka f = lambda x, y: x * y) bytecode สำหรับการเรียกฟังก์ชันจะเหมือนกับ g / f ในตัวอย่างก่อนหน้าของคุณดังนั้นประสิทธิภาพของแลมด้าจึงเหมือนกัน เป็นฟังก์ชัน def แลมด้าหรือ def ก็ไม่มีผลกระทบถ้าคุณใช้มันเหมือนกัน ทำผกผันประกาศฟังก์ชัน f () ภายในลูปแล้วเรียกมันว่า ...
tito

@tito ฉันเชื่อว่านั่นคือสิ่งที่ 3 ตัวอย่างที่กำหนดเวลาแสดงให้เห็น ...
Andy Hayden

@tito โอ้คุณกำลังพูดถึงการกำหนดฟังก์ชันในลูปแน่นอน แต่ฉันจะเถียงว่าเป็นรูปแบบที่ผิดปกติ ไม่แน่ใจว่าเหตุใดจึงต้องมีการโหวตลดความคิดเห็นนั้น ...
Andy Hayden

7

ประสิทธิภาพ:

การสร้างฟังก์ชั่นที่มีlambdaมีที่เร็วขึ้นเล็กน้อยdefกว่าการสร้างมันด้วย ความแตกต่างเกิดจากการdefสร้างรายการชื่อในตารางชาวบ้าน ฟังก์ชันผลลัพธ์มีความเร็วในการดำเนินการเท่ากัน


ความสามารถในการอ่าน:

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

# Using non-functional version.

heading(math.sqrt(v.x * v.x + v.y * v.y), math.atan(v.y / v.x))

# Using lambda with functional version.

fheading(v, lambda v: math.sqrt(v.x * v.x + v.y * v.y), lambda v: math.atan(v.y / v.x))

# Using def with functional version.

def size(v):
    return math.sqrt(v.x * v.x + v.y * v.y)

def direction(v):
    return math.atan(v.y / v.x)

deal_with_headings(v, size, direction)

อย่างที่คุณเห็นlambdaเวอร์ชันนั้นสั้นกว่าและ "ง่ายกว่า" ในแง่ที่คุณต้องเพิ่มลงlambda v:ในเวอร์ชันดั้งเดิมที่ไม่สามารถใช้งานได้เพื่อแปลงเป็นเวอร์ชันที่ใช้งานได้ ยังกระชับกว่ามาก แต่จำไว้ว่าผู้ใช้ Python จำนวนมากจะสับสนกับไวยากรณ์ของแลมบ์ดาดังนั้นสิ่งที่คุณสูญเสียไปในด้านความยาวและความซับซ้อนที่แท้จริงอาจได้รับความสับสนจากผู้เขียนโค้ดเพื่อน


ข้อ จำกัด :

  • lambda ฟังก์ชันสามารถใช้ได้เพียงครั้งเดียวเว้นแต่จะกำหนดให้กับชื่อตัวแปร
  • lambdaฟังก์ชันที่กำหนดให้กับชื่อตัวแปรไม่มีข้อได้เปรียบเหนือdefฟังก์ชัน
  • lambda ฟังก์ชั่นอาจเป็นเรื่องยากหรือเป็นไปไม่ได้ที่จะดอง
  • def ชื่อของฟังก์ชันต้องได้รับการคัดเลือกอย่างรอบคอบเพื่อให้สื่อความหมายได้อย่างสมเหตุสมผลและไม่ซ้ำกันหรืออย่างน้อยก็ไม่ได้ใช้ในขอบเขต

ความสม่ำเสมอ:

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


ฟังก์ชันที่มีอยู่แล้ว:

ตามที่ผู้อื่นระบุไว้การใช้งานหลายอย่างlambdaในสนามสามารถถูกแทนที่ได้โดยสมาชิกของoperatorโมดูลหรือโมดูลอื่น ๆ ตัวอย่างเช่น:

do_something(x, y, lambda x, y: x + y)
do_something(x, y, operator.add)

การใช้ฟังก์ชันที่มีอยู่แล้วสามารถทำให้โค้ดอ่านง่ายขึ้นในหลาย ๆ กรณี


หลักการของ Pythonic:“ ควรมีอย่างใดอย่างหนึ่งและควรมีเพียงวิธีเดียวเท่านั้นที่จะทำได้อย่างชัดเจน”

นั่นคล้ายกับหลักคำสอนแหล่งเดียวของความจริง น่าเสียดายที่หลักการ single-way-to-do-it ที่เห็นได้ชัดนั้นเป็นแรงบันดาลใจที่โหยหาสำหรับ Python มากกว่าที่จะเป็นแนวทางที่แท้จริง พิจารณาความเข้าใจอาร์เรย์ที่มีประสิทธิภาพมากใน Python เทียบเท่ากับฟังก์ชันmapและfilterฟังก์ชัน:

[e for e in some_array if some_condition(e)]
filter(some_array, some_condition)

lambdaและdefเหมือนกัน

เป็นเรื่องของความคิดเห็น แต่ฉันจะบอกว่าทุกสิ่งในภาษา Python มีไว้สำหรับการใช้งานทั่วไปซึ่งไม่ได้ทำลายอะไรอย่างชัดเจนคือ "Pythonic" เพียงพอ


7

เป็นที่ต้องการมากกว่า: ฟังก์ชันแลมบ์ดาหรือฟังก์ชันซ้อนกัน ( def)?

มีข้อดีอย่างหนึ่งในการใช้แลมบ์ดากับฟังก์ชันปกตินั่นคือสร้างขึ้นในนิพจน์

มีข้อบกพร่องหลายประการ:

  • ไม่มีชื่อ (เฉยๆ'<lambda>')
  • ไม่มี docstrings
  • ไม่มีคำอธิบายประกอบ
  • ไม่มีข้อความที่ซับซ้อน

ทั้งคู่ยังเป็นวัตถุประเภทเดียวกัน ด้วยเหตุผลดังกล่าวโดยทั่วไปฉันชอบสร้างฟังก์ชันด้วยdefคีย์เวิร์ดแทนที่จะใช้ lambdas

จุดแรก - เป็นวัตถุประเภทเดียวกัน

แลมบ์ดาส่งผลให้อ็อบเจ็กต์ประเภทเดียวกันกับฟังก์ชันปกติ

>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
... 
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True

เนื่องจาก lambdas เป็นฟังก์ชันจึงเป็นวัตถุชั้นหนึ่ง

ทั้ง lambdas และฟังก์ชั่น:

  • สามารถส่งผ่านไปรอบ ๆ เป็นอาร์กิวเมนต์ (เช่นเดียวกับฟังก์ชันทั่วไป)
  • เมื่อสร้างขึ้นภายในฟังก์ชันภายนอกจะกลายเป็นการปิดทับท้องถิ่นของฟังก์ชันภายนอกนั้น

แต่โดยค่าเริ่มต้น lambdas ขาดบางสิ่งที่ฟังก์ชันได้รับผ่านไวยากรณ์นิยามฟังก์ชันแบบสมบูรณ์

lamba ของ __name__คือ'<lambda>'

Lambdas เป็นฟังก์ชันที่ไม่ระบุชื่อดังนั้นพวกเขาจึงไม่รู้ชื่อของตัวเอง

>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'

ดังนั้นแลมบ์ดาจึงไม่สามารถค้นหาด้วยโปรแกรมในเนมสเปซได้

สิ่งนี้ จำกัด บางสิ่ง ตัวอย่างเช่นfooสามารถค้นหาได้ด้วยรหัสซีเรียลในขณะที่lไม่สามารถ:

>>> import pickle
>>> pickle.loads(pickle.dumps(l))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7fbbc0464e18>: 
attribute lookup <lambda> on __main__ failed

เราสามารถค้นหาได้fooดี - เพราะรู้จักชื่อของตัวเอง:

>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>

Lambdas ไม่มีคำอธิบายประกอบและไม่มี docstring

โดยทั่วไปแลมบ์ดาไม่ได้รับการจัดทำเป็นเอกสาร มาเขียนใหม่fooให้เป็นเอกสารกันดีกว่า:

def foo() -> int:
    """a nullary function, returns 0 every time"""
    return 0

ตอนนี้ foo มีเอกสารประกอบ:

>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:

foo() -> int
    a nullary function, returns 0 every time

ในขณะที่เราไม่มีกลไกเดียวกันในการให้ข้อมูลเดียวกันกับ lambdas:

>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda (...)

แต่เราสามารถแฮ็คได้ที่:

>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda ) -> in
    nullary -> 0

แต่อาจมีข้อผิดพลาดบางอย่างที่ทำให้ผลลัพธ์ของความช่วยเหลือยุ่งเหยิง

Lambdas สามารถส่งคืนนิพจน์เท่านั้น

Lambdas ไม่สามารถส่งคืนคำสั่งที่ซับซ้อนได้มีเพียงนิพจน์เท่านั้น

>>> lambda: if True: 0
  File "<stdin>", line 1
    lambda: if True: 0
             ^
SyntaxError: invalid syntax

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

เราใช้ Python เพื่อความชัดเจนและการบำรุงรักษา การใช้แลมบ์ดามากเกินไปสามารถต่อต้านสิ่งนั้นได้

อัพไซด์เดียวสำหรับ lambdas: สามารถสร้างได้ในนิพจน์เดียว

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

การสร้างฟังก์ชันภายในการเรียกใช้ฟังก์ชันหลีกเลี่ยงการค้นหาชื่อ (ราคาไม่แพง) เมื่อเทียบกับฟังก์ชันที่สร้างขึ้นจากที่อื่น

อย่างไรก็ตามเนื่องจาก Python ได้รับการประเมินอย่างเคร่งครัดจึงไม่มีการเพิ่มประสิทธิภาพอื่น ๆ นอกเหนือจากการหลีกเลี่ยงการค้นหาชื่อ

เพื่อการแสดงออกที่เรียบง่ายฉันอาจเลือกแลมด้า

ฉันมักจะใช้ lambdas เมื่อทำ Python แบบโต้ตอบเพื่อหลีกเลี่ยงหลายบรรทัดเมื่อจะทำ ฉันใช้รูปแบบรหัสต่อไปนี้เมื่อฉันต้องการส่งผ่านอาร์กิวเมนต์ไปยังตัวสร้างเมื่อเรียกtimeit.repeat:

import timeit

def return_nullary_lambda(return_value=0):
    return lambda: return_value

def return_nullary_function(return_value=0):
    def nullary_fn():
        return return_value
    return nullary_fn

และตอนนี้:

>>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
0.24312214995734394
>>> min(timeit.repeat(lambda: return_nullary_function(1)))
0.24894469301216304

ผมเชื่อว่าแตกต่างเวลาเล็กน้อยข้างต้นสามารถนำมาประกอบกับการค้นหาชื่อreturn_nullary_function- ทราบว่ามันเป็นมากเล็กน้อย

สรุป

Lambdas เหมาะสำหรับสถานการณ์ที่ไม่เป็นทางการที่คุณต้องการย่อบรรทัดของโค้ดเพื่อให้เกิดจุดเอกพจน์

Lambdas ไม่ดีสำหรับสถานการณ์ที่เป็นทางการมากขึ้นซึ่งคุณต้องการความชัดเจนสำหรับผู้แก้ไขโค้ดที่จะมาในภายหลังโดยเฉพาะอย่างยิ่งในกรณีที่ไม่เป็นเรื่องเล็กน้อย

เรารู้ว่าเราควรจะให้ชื่อที่ดีแก่วัตถุของเรา เราจะทำได้อย่างไรเมื่อวัตถุไม่มีชื่อ

ด้วยเหตุผลทั้งหมดนี้ฉันมักชอบสร้างฟังก์ชันด้วยdefแทนที่จะสร้างด้วยlambda.


6

ผมเห็นด้วยกับคำแนะนำของ nosklo: defถ้าคุณต้องการที่จะให้ฟังก์ชั่นชื่อการใช้งาน ฉันจองlambdaฟังก์ชันสำหรับกรณีที่ฉันแค่ส่งข้อมูลโค้ดสั้น ๆ ไปยังฟังก์ชันอื่นเช่น:

a = [ (1,2), (3,4), (5,6) ]
b = map( lambda x: x[0]+x[1], a )

3
ในการผสมผสานแผนที่ / แลมบ์ดาส่วนใหญ่คุณสามารถแทนที่ด้วยความเข้าใจในรายการหรือฟังก์ชันที่เหมาะสมกว่า ตัวอย่างเช่น "map (sum, a)" หรือ "[x [0] + x [1] for x in a]"
John Millikin

ใช่นั่นคือเรื่องจริง บางครั้งฉันชอบแผนที่ () มากกว่า นี่เป็นเพียงตัวอย่างที่สร้างขึ้นจากการใช้ฟังก์ชันในบรรทัด
Dan Lenski

อย่างแน่นอน ... ตัวอย่างส่วนใหญ่ถูกสร้างขึ้นเนื่องจากเป็นการใช้งานที่ผิดธรรมชาติและมีวิธีที่ดีกว่าในทางปฏิบัติ
nosklo

5

ในขณะที่เห็นด้วยกับคำตอบอื่น ๆ บางครั้งก็สามารถอ่านได้มากกว่า นี่คือตัวอย่างที่ซึ่งlambdaมีประโยชน์ในกรณีที่ใช้ฉันให้เผชิญหน้าของ N defaultdictมิติ
นี่คือตัวอย่าง:

from collections import defaultdict
d = defaultdict(lambda: defaultdict(list))
d['Foo']['Bar'].append(something)

ฉันคิดว่ามันอ่านง่ายกว่าการสร้างdefมิติที่สอง สิ่งนี้มีความสำคัญมากยิ่งขึ้นสำหรับมิติข้อมูลที่สูงขึ้น


from functools import partial; defaultdict(partial(defaultdict, list)). กำหนดชื่อบางส่วนหากคุณต้องการใช้มากกว่าหนึ่งครั้ง แต่ถ้าคุณยังคงพบโครงสร้างนี้แสดงว่าคุณไม่แห้ง แยกตัวประกอบลงในไลบรารียูทิลิตี้ คุณสามารถใช้โครงสร้างนี้เพื่อสร้างค่าเริ่มต้น n มิติโดยใช้ฟังก์ชันอื่น ๆ (หรือการวนซ้ำหรือการเรียกซ้ำ)
DylanYoung

3

การใช้แลมด้าเป็นหลักสำหรับฟังก์ชันการเรียกกลับอย่างง่ายเสมอมาและสำหรับแผนที่, ลด, กรองซึ่งต้องใช้ฟังก์ชันเป็นอาร์กิวเมนต์ ด้วยความเข้าใจในรายการกลายเป็นบรรทัดฐานและอนุญาตให้เพิ่มเข้ามาในกรณี:

x = [f for f in range(1, 40) if f % 2]

มันยากที่จะจินตนาการถึงกรณีจริงสำหรับการใช้แลมด้าในชีวิตประจำวัน ด้วยเหตุนี้ฉันจึงบอกว่าให้หลีกเลี่ยงแลมด้าและสร้างฟังก์ชันซ้อนกัน


3

ข้อ จำกัด ที่สำคัญของ lambdas คือไม่มีอะไรเลยนอกจากนิพจน์ แทบจะเป็นไปไม่ได้เลยที่การแสดงออกของแลมบ์ดาจะก่อให้เกิดสิ่งใด ๆ นอกจากผลข้างเคียงเล็กน้อยเนื่องจากไม่สามารถมีส่วนใดใกล้เคียงกับร่างกายที่อุดมสมบูรณ์เท่ากับdefฟังก์ชัน 'ed

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

แก้ไข:นี่เป็นคำถามที่ค่อนข้างเก่าและความคิดเห็นของฉันเกี่ยวกับเรื่องนี้ก็เปลี่ยนไปบ้าง

ก่อนอื่นฉันมีอคติอย่างมากกับการกำหนดlambdaนิพจน์ให้กับตัวแปร เนื่องจาก python มีไวยากรณ์พิเศษสำหรับสิ่งนั้น (hint, def) นอกจากนั้นการใช้แลมด้าจำนวนมากแม้ว่าจะไม่ได้รับชื่อ แต่ก็มีการใช้งานที่กำหนดไว้ล่วงหน้า (และมีประสิทธิภาพมากขึ้น) ยกตัวอย่างเช่นในคำถามที่สามารถย่อเพียง(1).__add__โดยไม่จำเป็นที่จะต้องห่อไว้ในหรือlambda defหลายคนใช้กันทั่วไปอื่น ๆ สามารถพอใจกับการรวมกันของบางoperator, itertoolsและfunctoolsโมดูล


1
(1).__add__- การเรียกวิธีการดักฟังโดยตรงแทบจะไม่เกิดขึ้นเลย หนึ่งพันlambdaวินาทีสำหรับการโทรหาคู่หูโดยตรงแต่ละครั้ง
Ethan Furman

1
@EthanFurman: จากประสบการณ์ของฉันการเรียกธรรมชาติ(1).__add__ค่อนข้างแปลก แต่ฉันจะไม่ไปไหนใกล้ "ควร" lambda x: 1 + xโดยไม่ต้องสงสัยผมพบว่าอดีตจะเป็นอย่างมากมายสามารถอ่านได้ ถ้าเรามีอะไรที่คล้ายกับสัญกรณ์ชิ้นส่วน haskells (1+)มันจะดีมาก แต่เราต้องทำกับสิ่งที่มีความหมายตรงตามความหมายชื่อเมธอด dunder
SingleNegationElimination

2
  • เวลาคำนวณ
  • ฟังก์ชันที่ไม่มีชื่อ
  • เพื่อให้บรรลุหนึ่งฟังก์ชันและฟังก์ชันการใช้งานมากมาย

พิจารณาตัวอย่างง่ายๆ

# CREATE ONE FUNCTION AND USE IT TO PERFORM MANY OPERATIONS ON SAME TYPE OF DATA STRUCTURE.
def variousUse(a,b=lambda x:x[0]):
    return [b(i) for i in a]

dummyList = [(0,1,2,3),(4,5,6,7),(78,45,23,43)]
variousUse(dummyList)                           # extract first element
variousUse(dummyList,lambda x:[x[0],x[2],x[3]]) # extract specific indexed element
variousUse(dummyList,lambda x:x[0]+x[2])        # add specific elements
variousUse(dummyList,lambda x:x[0]*x[2])        # multiply specific elements

1

หากคุณกำลังจะกำหนดแลมด้าให้กับตัวแปรในขอบเขตโลคัลคุณสามารถใช้ def ได้เช่นกันเพราะอ่านได้ง่ายกว่าและสามารถขยายได้ง่ายขึ้นในอนาคต:

fun = lambda a, b: a ** b # a pointless use of lambda
map(fun, someList)

หรือ

def fun(a, b): return a ** b # more readable
map(fun, someList)

ทั้งสองfrom operator import pow;map(pow, someList)และ(a**b for a,b in someList)ยังอ่านได้มากขึ้น
InQβ

1

ฉันพบการใช้ lambdas อย่างหนึ่ง ... อยู่ในข้อความดีบัก

เนื่องจาก lambdas สามารถประเมินได้อย่างเฉื่อยชาคุณสามารถมีรหัสดังนี้:

log.debug(lambda: "this is my message: %r" % (some_data,))

แทนที่จะแพง:

log.debug("this is my message: %r" % (some_data,))

ซึ่งประมวลผลสตริงรูปแบบแม้ว่าการเรียกดีบักจะไม่สร้างเอาต์พุตเนื่องจากระดับการบันทึกปัจจุบัน

แน่นอนว่ามันจะทำงานได้ตามที่อธิบายไว้โมดูลการบันทึกที่ใช้งานจะต้องรองรับ lambdas เป็น "lazy parameters" (เหมือนที่โมดูลการบันทึกของฉันทำ)

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

ตัวอย่างเช่นตัวดำเนินการ ternary ที่กำหนดเองนี้:

def mif(condition, when_true, when_false):
    if condition:
         return when_true()
    else:
         return when_false()

mif(a < b, lambda: a + a, lambda: b + b)

แทน:

def mif(condition, when_true, when_false):
    if condition:
         return when_true
    else:
         return when_false

mif(a < b, a + a, b + b)

ด้วย lambdas เฉพาะนิพจน์ที่เลือกโดยเงื่อนไขเท่านั้นที่จะได้รับการประเมินโดยจะไม่มีการประเมินทั้ง lambdas

แน่นอนว่าคุณสามารถใช้ฟังก์ชันแทน lambdas ได้ แต่สำหรับ lambdas นิพจน์สั้น ๆ นั้น (c) leaner


1
NB loggingมีการจัดรูปแบบขี้เกียจอยู่แล้ว: log.debug("this is my message: %r", some_data)จะจัดรูปแบบเมื่อ / หากมีการร้องขอข้อความเท่านั้น
j08lue

@ j08lue วิธี lambda จะข้ามการประเมินทุกอย่างในกรณีที่ไม่ได้สร้างเอาต์พุตการดีบักในกรณีที่คุณแสดงว่าsome_dataอาจเป็นการเรียกใช้นิพจน์หรือฟังก์ชัน / วิธีการที่มีราคาแพง
Glushiator

0

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

เช่น :

คุณมีฟังก์ชันที่มีลายเซ็นนี้: myFunction (data, callback function)

คุณต้องการส่งผ่านฟังก์ชันที่เพิ่ม 2 องค์ประกอบ

ใช้แลมด้า:

myFunction(data, (lambda x, y : x + y))

วิธี pythonic:

import operator
myFunction(data, operator.add)

หรือแน่นอนว่านี่เป็นตัวอย่างง่ายๆ แต่มีหลายสิ่งหลายอย่างที่โมดูลตัวดำเนินการจัดเตรียมไว้ให้รวมถึงรายการ setters / getters สำหรับ list และ dict เด็ดจริง.


-1

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

my_list.sort(key=lambda o: o.x)

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


-2

แลมบ์ดามีประโยชน์ในการสร้างฟังก์ชันใหม่:

>>> def somefunc(x): return lambda y: x+y
>>> f = somefunc(10)
>>> f(2)
12
>>> f(4)
14
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.