Pertanyaan Mana yang lebih disukai untuk digunakan dalam Python: fungsi lambda atau fungsi bertingkat ('def')?


Saya kebanyakan menggunakan fungsi lambda tetapi terkadang menggunakan fungsi bertingkat yang tampaknya memberikan perilaku yang sama.

Berikut adalah beberapa contoh sepele di mana mereka secara fungsional melakukan hal yang sama jika ditemukan dalam fungsi lain:

Fungsi Lambda

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

Fungsi bertingkat

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

>>> b(5)
6

Apakah ada manfaat menggunakan salah satu dari yang lain? (Kinerja? Keterbacaan? Keterbatasan? Konsistensi? Dll)

Apakah itu penting? Jika tidak maka hal itu melanggar prinsip Pythonic:

“Harus ada satu — dan sebaiknya hanya satu — cara yang jelas untuk melakukannya”.


75
2017-09-25 17:15


asal


Jawaban:


Jika Anda perlu menetapkan lambda ke nama, gunakan a def sebagai gantinya. defhanyalah gula sintaksis untuk tugas, jadi hasilnya sama, dan mereka jauh lebih fleksibel dan mudah dibaca.

lambdas dapat digunakan untuk gunakan sekali, buang fungsi yang tidak memiliki nama.

Namun, kasus penggunaan ini sangat jarang. Anda jarang perlu melewatkan objek fungsi yang tidak disebutkan namanya.

The builtins map() dan filter() membutuhkan objek fungsi, tapi daftar pemahaman dan ekspresi generator umumnya lebih mudah dibaca daripada fungsi-fungsi itu dan dapat mencakup semua kasus penggunaan, tanpa perlu lambdas.

Untuk kasus-kasus Anda benar-benar membutuhkan objek fungsi kecil, Anda harus menggunakan operator fungsi modul, seperti operator.add dari pada lambda x, y: x + y

Jika Anda masih membutuhkannya lambda tidak tercakup, Anda mungkin mempertimbangkan untuk menulis a def, agar lebih mudah dibaca. Jika fungsinya lebih kompleks daripada yang ada di operator modul, a def mungkin lebih baik.

Jadi, dunia nyata bagus lambda kasus penggunaan sangat jarang.


84
2017-09-25 17:16



Secara praktis, bagi saya ada dua perbedaan:

Yang pertama adalah tentang apa yang mereka lakukan dan apa yang mereka kembalikan:

  • def adalah kata kunci yang tidak mengembalikan apa pun dan membuat 'nama' di ruang nama lokal.

  • lambda adalah kata kunci yang mengembalikan objek fungsi dan tidak membuat 'nama' di ruang nama lokal.

Oleh karena itu, jika Anda perlu memanggil fungsi yang mengambil objek fungsi, satu-satunya cara untuk melakukan itu dalam satu baris kode python adalah dengan lambda. Tidak ada yang setara dengan def.

Dalam beberapa kerangka kerja ini sebenarnya cukup umum; misalnya, saya gunakan Memutar banyak, dan melakukan sesuatu seperti itu

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

cukup umum, dan lebih ringkas dengan lambdas.

Perbedaan kedua adalah tentang apa fungsi sebenarnya yang diizinkan untuk dilakukan.

  • Fungsi yang didefinisikan dengan 'def' dapat berisi kode python apa pun
  • Fungsi yang didefinisikan dengan 'lambda' harus dievaluasi ke ekspresi, dan dengan demikian tidak dapat berisi pernyataan seperti cetak, impor, ...

Sebagai contoh,

def p(x): print x

berfungsi seperti yang diharapkan, sementara

lambda x: print x

adalah SyntaxError.

Tentu saja, ada solusi - pengganti print dengan sys.stdout.write, atau import dengan __import__. Tetapi biasanya Anda lebih baik menggunakan fungsi dalam kasus itu.


25
2017-09-26 10:20



Dalam wawancara ini, Guido van Rossum mengatakan dia berharap dia tidak membiarkan 'lambda' menjadi Python:

"T. Apa fitur Python yang paling tidak Anda sukai?

  Terkadang saya terlalu cepat dalam menerima sumbangan, dan kemudian menyadari bahwa itu adalah kesalahan. Salah satu contohnya adalah beberapa fitur pemrograman fungsional, seperti fungsi lambda. lambda adalah kata kunci yang memungkinkan Anda membuat fungsi anonim kecil; fungsi built-in seperti peta, filter, dan mengurangi menjalankan fungsi melalui tipe urutan, seperti daftar.

  Dalam prakteknya, itu tidak berubah dengan baik. Python hanya memiliki dua cakupan: lokal dan global. Ini membuat tulisan lambda berfungsi menyakitkan, karena Anda sering ingin mengakses variabel dalam ruang lingkup di mana lambda didefinisikan, tetapi Anda tidak bisa karena dua cakupan. Ada jalan di sekitar ini, tapi itu sesuatu dari sebuah kludge. Seringkali tampaknya jauh lebih mudah dalam Python untuk hanya menggunakan untuk loop bukannya main-main dengan fungsi lambda. peta dan teman bekerja dengan baik hanya ketika sudah ada fungsi bawaan yang melakukan apa yang Anda inginkan.

IMHO, Iambdas kadang-kadang bisa nyaman, tetapi biasanya nyaman dengan mengorbankan keterbacaan. Dapatkah Anda memberi tahu saya apa yang dilakukannya:

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

Saya menulisnya, dan saya butuh satu menit untuk mengetahuinya. Ini dari Project Euler - saya tidak akan mengatakan masalah apa karena saya benci spoiler, tetapi berjalan dalam 0,124 detik :)


17
2017-09-25 17:29



Untuk n = 1000 inilah beberapa waktu untuk memanggil fungsi vs lambda:

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

9
2017-08-16 13:16



Saya setuju dengan saran nosklo: jika Anda perlu memberi nama pada fungsi tersebut, gunakan def. Saya memesan lambda berfungsi untuk kasus-kasus di mana saya hanya melewatkan cuplikan singkat kode ke fungsi lain, misalnya:

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

6
2017-09-25 17:19



Kinerja:

Membuat fungsi dengan lambda aku s sedikit lebih cepat daripada membuatnya def. Perbedaannya adalah karena def membuat entri nama di tabel penduduk setempat. Fungsi yang dihasilkan memiliki kecepatan eksekusi yang sama.


Keterbacaan:

Fungsi Lambda agak kurang terbaca untuk sebagian besar pengguna Python, tetapi juga lebih ringkas dalam beberapa keadaan. Pertimbangkan untuk mengubah dari penggunaan rutin non-fungsional ke fungsional:

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

Seperti yang Anda lihat, lambda versi lebih pendek dan "lebih mudah" dalam arti bahwa Anda hanya perlu menambahkan lambda v: ke versi asli non-fungsional untuk dikonversi ke versi fungsional. Ini juga jauh lebih ringkas. Tapi ingat, banyak pengguna Python akan bingung oleh sintaks lambda, jadi apa yang Anda kehilangan panjang dan kompleksitas nyata mungkin diperoleh kembali dalam kebingungan dari sesama coders.


Keterbatasan:

  • lambda fungsi hanya dapat digunakan sekali, kecuali ditetapkan ke nama variabel.
  • lambda fungsi yang ditetapkan untuk nama variabel tidak memiliki kelebihan def fungsi.
  • lambda fungsi bisa sulit atau tidak mungkin menjadi acar.
  • def Nama-nama fungsi 'harus dipilih secara hati-hati agar cukup deskriptif dan unik atau setidaknya jika tidak digunakan dalam ruang lingkup.

Konsistensi:

Python kebanyakan menghindari konvensi pemrograman fungsional dalam mendukung semantik objektif prosedural dan lebih sederhana. Itu lambdaoperator berdiri dalam kontras langsung dengan bias ini. Apalagi sebagai alternatif yang sudah lazim def, yang lambda fungsi menambah keragaman ke sintaks Anda. Beberapa orang akan menganggap itu kurang konsisten.


Fungsi yang sudah ada sebelumnya:

Seperti dicatat oleh orang lain, banyak menggunakan lambda di lapangan dapat digantikan oleh anggota operator atau modul lain. Contohnya:

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

Menggunakan fungsi yang sudah ada dapat membuat kode lebih mudah dibaca dalam banyak kasus.


Prinsip Pythonic: “Harus ada satu — dan sebaiknya hanya satu — cara yang jelas untuk melakukannya”

Itu mirip dengan satu sumber kebenaran doktrin. Sayangnya, prinsip satu-jelas-cara-untuk-melakukan-itu selalu lebih merupakan aspirasi sedih untuk Python, daripada prinsip panduan yang benar. Pertimbangkan pemahaman array yang sangat kuat dengan Python. Mereka secara fungsional setara dengan map dan filter fungsi:

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

lambda dan def adalah sama.

Ini masalah pendapat, tapi saya akan mengatakan bahwa apapun dalam bahasa Python yang dimaksudkan untuk penggunaan umum yang tidak jelas merusak apa pun adalah "Pythonic" cukup.


5
2018-02-11 18:14



Penggunaan utama lambda selalu untuk fungsi panggilan balik sederhana, dan untuk memetakan, mengurangi, memfilter, yang membutuhkan fungsi sebagai argumen. Dengan daftar pemahaman menjadi norma, dan tambahan diizinkan jika seperti di:

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

sulit membayangkan kasus nyata untuk penggunaan lambda dalam penggunaan sehari-hari. Akibatnya, saya akan mengatakan, hindari lambda dan buat fungsi bertingkat.


4
2017-09-25 19:13



Keterbatasan penting lambdas adalah bahwa mereka tidak dapat mengandung apa pun selain ekspresi. Hampir tidak mungkin untuk ekspresi lambda untuk menghasilkan sesuatu selain efek samping sepele, karena tidak dapat memiliki tubuh yang kaya sama seperti deffungsi ed.

Dikatakan demikian, Lua mempengaruhi gaya pemrograman saya terhadap penggunaan ekstensif fungsi anonim, dan saya mengotori kode saya dengan mereka. Di atas itu, saya cenderung berpikir tentang peta / mengurangi sebagai operator abstrak dengan cara saya tidak mempertimbangkan pemahaman daftar atau generator, hampir sama seperti jika saya menunda keputusan implementasi secara eksplisit dengan menggunakan operator tersebut.

Edit: Ini adalah pertanyaan yang cukup lama, dan pendapat saya tentang masalah ini telah berubah.

Pertama, saya sangat bias terhadap menugaskan lambda ekspresi ke variabel; karena python memiliki sintaks khusus hanya untuk itu (petunjuk, def). Selain itu, banyak penggunaan untuk lambda, bahkan ketika mereka tidak mendapatkan nama, memiliki implementasi yang telah ditentukan (dan lebih efisien). Misalnya, contoh yang dimaksud dapat disingkat menjadi adil (1).__add__, tanpa perlu membungkusnya dalam lambda atau def. Banyak kegunaan umum lainnya dapat dipenuhi dengan beberapa kombinasi dari operator, itertools dan functools modul.


3
2018-06-17 00:02



Sambil menyetujui dengan jawaban lainnya, terkadang itu lebih mudah dibaca. Inilah contoh di mana lambda sangat berguna, dalam kasus penggunaan saya terus menghadapi dimensi N defaultdict.
Inilah contohnya:

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

Saya merasa lebih mudah dibaca daripada membuat defuntuk dimensi kedua. Ini bahkan lebih signifikan untuk dimensi yang lebih tinggi.


3
2017-12-27 10:54



Satu penggunaan untuk lambdas yang saya temukan ... ada dalam pesan debug.

Karena lambdas dapat dengan malas dievaluasi, Anda dapat memiliki kode seperti ini:

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

bukannya mungkin mahal:

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

yang memproses string format bahkan jika panggilan debug tidak menghasilkan output karena level logging saat ini.

Tentu saja agar berfungsi sebagaimana dijelaskan, modul logging yang digunakan harus mendukung lambdas sebagai "parameter malas" (seperti yang dilakukan oleh modul logging saya).

Ide yang sama dapat diterapkan pada setiap kasus evaluasi malas lainnya untuk penciptaan nilai konten permintaan.

Misalnya operator terner khusus ini:

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)

dari pada:

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

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

dengan lambdas hanya ekspresi yang dipilih oleh kondisi akan dievaluasi, tanpa lambdas keduanya akan dievaluasi.

Tentu saja Anda bisa menggunakan fungsi alih-alih lambdas, tetapi untuk lambdas ekspresi pendek adalah (c) lebih ramping.


2
2017-10-21 21:37



Lebih disukai: fungsi lambda atau fungsi bertingkat (def)?

Ada satu keuntungan menggunakan lambda melalui fungsi biasa (mereka dibuat dalam ekspresi), dan beberapa kelemahan. Untuk alasan itu, saya lebih suka membuat fungsi dengan def kata kunci alih-alih dengan lambdas.

Titik pertama - mereka adalah jenis objek yang sama

Lambda menghasilkan jenis objek yang sama dengan fungsi biasa

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

Karena lambdas adalah fungsi, mereka adalah objek kelas satu.

Baik lambdas dan fungsi:

  • dapat diedarkan sebagai argumen (sama dengan fungsi biasa)
  • ketika dibuat dalam fungsi luar menjadi penutupan atas penduduk setempat yang luar biasa itu

Tapi lambdas, secara default, kehilangan beberapa hal yang berfungsi melalui sintaks definisi fungsi penuh.

A lamba __name__ aku s '<lambda>'

Lambdas adalah fungsi anonim, setelah semua, jadi mereka tidak tahu nama mereka sendiri.

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

Jadi lambda tidak dapat dilihat secara terprogram di ruang nama mereka.

Ini membatasi hal-hal tertentu. Sebagai contoh, foo dapat dicari dengan kode serial, sementara l tidak bisa:

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

Kami dapat mencari foo baik-baik saja - karena ia tahu namanya sendiri:

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

Lambdas tidak memiliki penjelasan dan tidak ada dokumen

Pada dasarnya, lambdas tidak didokumentasikan. Mari menulis ulang foo untuk didokumentasikan dengan lebih baik:

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

Sekarang, foo memiliki dokumentasi:

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

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

Padahal, kita tidak memiliki mekanisme yang sama untuk memberikan informasi yang sama kepada lambdas:

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

<lambda> lambda (...)

Tetapi kami dapat meretasnya:

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

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

Namun, mungkin ada beberapa kesalahan yang mengacaukan keluaran bantuan.

Lambdas hanya bisa mengembalikan ekspresi

Lambdas tidak dapat mengembalikan pernyataan kompleks, hanya ekspresi.

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

Ekspresi dapat diakui agak rumit, dan jika Anda mencoba sangat keras Anda mungkin bisa mencapai hal yang sama dengan lambda, tetapi kerumitan tambahan lebih merupakan kerugian untuk menulis kode yang jelas.

Kami menggunakan Python untuk kejelasan dan pemeliharaan. Terlalu sering menggunakan lambdas dapat bekerja melawan itu.

Itu hanyaterbalik untuk lambdas: dapat dibuat dalam satu ekspresi tunggal

Ini adalah satu-satunya kemungkinan terbalik. Karena Anda dapat membuat lambda dengan ekspresi, Anda dapat membuatnya di dalam panggilan fungsi.

Membuat fungsi di dalam panggilan fungsi menghindari pencarian nama (murah) dibandingkan dengan yang dibuat di tempat lain.

Namun, karena Python secara ketat dievaluasi, tidak ada keuntungan kinerja lain untuk melakukannya selain menghindari pencarian nama.

Untuk ekspresi yang sangat sederhana, saya mungkin memilih lambda.

Saya juga cenderung menggunakan lambdas ketika melakukan Python interaktif, untuk menghindari beberapa baris ketika seseorang akan melakukannya. Saya menggunakan jenis format kode berikut ketika saya ingin menyampaikan argumen ke konstruktor ketika menelepon 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

Dan sekarang:

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

Saya yakin sedikit perbedaan waktu di atas dapat dikaitkan dengan pencarian nama di return_nullary_function - perhatikan itu sangat dapat diabaikan.

Kesimpulan

Lambdas baik untuk situasi informal di mana Anda ingin meminimalkan baris kode yang mendukung pembuatan titik tunggal.

Lambdas buruk untuk situasi yang lebih formal di mana Anda perlu kejelasan untuk editor kode yang akan datang nanti, terutama dalam kasus di mana mereka tidak sepele.

Kami tahu kami seharusnya memberi nama baik pada benda-benda kami. Bagaimana kita bisa melakukannya ketika objek itu tidak nama?

Untuk semua alasan ini, saya lebih suka membuat fungsi dengan def alih-alih dengan lambda.


2
2018-03-27 19:03