E731 tidak menetapkan ekspresi lambda, gunakan def

193

Saya mendapat peringatan pep8 ini setiap kali saya menggunakan ekspresi lambda. Apakah ungkapan lambda tidak disarankan? Jika tidak mengapa?

Kechit Goyal
sumber
4
Untuk kejelasan, pertanyaan mengacu pada pesan untuk check-in otomatis flake8( flake8.pycqa.org )
rakslice

Jawaban:

232

Rekomendasi dalam PEP-8 yang Anda temui adalah:

Selalu gunakan pernyataan def alih-alih pernyataan tugas yang mengikat ekspresi lambda langsung ke nama.

Iya:

def f(x): return 2*x 

Tidak:

f = lambda x: 2*x 

Bentuk pertama berarti bahwa nama objek fungsi yang dihasilkan secara khusus 'f' bukan generik '<lambda>'. Ini lebih berguna untuk traceback dan representasi string secara umum. Penggunaan pernyataan penugasan menghilangkan satu-satunya manfaat ekspresi lambda dapat menawarkan lebih dari pernyataan def eksplisit (yaitu bahwa hal itu dapat tertanam dalam ekspresi yang lebih besar)

Menugaskan lambdas untuk nama pada dasarnya hanya menduplikasi fungsi def- dan secara umum, yang terbaik adalah melakukan sesuatu dengan satu cara untuk menghindari kebingungan dan meningkatkan kejelasan.

Use case yang sah untuk lambda adalah di mana Anda ingin menggunakan suatu fungsi tanpa menugaskannya, misalnya:

sorted(players, key=lambda player: player.rank)

Secara umum, argumen utama yang menentang melakukan ini adalah bahwa defpernyataan akan menghasilkan lebih banyak baris kode. Tanggapan utama saya untuk itu adalah: ya, dan itu bagus. Kecuali jika Anda bermain golf kode, meminimalkan jumlah garis bukanlah sesuatu yang harus Anda lakukan: lakukan dengan singkat.

Gareth Latty
sumber
5
Saya tidak melihat bagaimana ini lebih buruk. Traceback masih akan menyertakan nomor baris yang salah dan file sumber. Yang satu mungkin mengatakan "f" sedangkan yang lain mengatakan "lambda". Mungkin kesalahan lambda lebih mudah untuk dipindai karena itu bukan nama fungsi satu karakter, atau nama panjang yang tidak disebutkan namanya?
g33kz0r
4
@ g33kz0r Yah, tentu saja, jika Anda menganggap kode Anda lainnya memiliki kualitas buruk, konvensi berikut tidak akan banyak menguntungkan Anda. Secara umum, tidak, ini bukan akhir dunia, tapi itu masih ide yang buruk.
Gareth Latty
40
Jawaban ini sangat tidak membantu, karena ketika menjalankan pendekatan yang disarankan menggunakan defmelalui pemeriksa PEP8, Anda dapatkan E704 multiple statements on one line (def), dan jika Anda membaginya menjadi dua baris Anda mendapatkan E301 expected 1 blank line, found 0: - /
Adam Spires
4
Saya setuju itu harus dibagi. Poin saya adalah bahwa a) tidak terpecah dalam kode jawaban di atas, menyebabkan E704, dan b) jika Anda membaginya, Anda perlu baris kosong jelek di atasnya untuk menghindari E301.
Adam Spires
3
Saya menggunakan lambdas ketika saya ingin menekankan fungsi murni (tanpa efek samping), dan kadang-kadang saya harus menggunakan fungsi yang sama di dua tempat, yaitu groupby dan menyortir bersama. Jadi saya mengabaikan konvensi ini.
manu
119

Begini ceritanya, saya punya fungsi lambda sederhana yang saya gunakan dua kali.

a = map(lambda x : x + offset, simple_list)
b = map(lambda x : x + offset, another_simple_list)

Ini hanya untuk representasi, saya telah menghadapi beberapa versi yang berbeda ini.

Sekarang, untuk menjaga keadaan tetap kering, saya mulai menggunakan kembali lambda umum ini.

f = lambda x : x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)

Pada titik ini pemeriksa kualitas kode saya mengeluh tentang lambda menjadi fungsi bernama jadi saya mengubahnya menjadi fungsi.

def f(x):
    return x + offset
a = map(f, simple_list)
b = map(f, another_simple_list)

Sekarang pemeriksa mengeluh bahwa suatu fungsi harus dibatasi oleh satu baris kosong sebelum dan sesudah.

def f(x):
    return x + offset

a = map(f, simple_list)
b = map(f, another_simple_list)

Di sini kita sekarang memiliki 6 baris kode bukannya 2 baris asli tanpa peningkatan keterbacaan dan tidak ada peningkatan menjadi pythonic. Pada titik ini pemeriksa kode mengeluh tentang fungsi tidak memiliki dokumen.

Menurut pendapat saya aturan ini sebaiknya dihindari dan dilanggar ketika masuk akal, gunakan penilaian Anda.

iankit
sumber
13
a = [x + offset for x in simple_list]. Tidak perlu digunakan mapdan di lambdasini.
Georgy
8
@ Georgy Saya percaya intinya adalah untuk memindahkan x + offsetbagian ke lokasi abstrak yang dapat diperbarui tanpa mengubah lebih dari satu baris kode. Dengan pemahaman daftar seperti yang Anda sebutkan, Anda masih membutuhkan dua baris kode yang berisi kode-kode itu yang x + offsetsekarang berada dalam daftar pemahaman. Untuk menariknya keluar seperti yang diinginkan penulis, Anda memerlukan a defatau lambda.
Julian
1
@Julian Terlepas dari defdan lambdaseseorang juga bisa menggunakan functools.partial : f = partial(operator.add, offset)dan kemudian a = list(map(f, simple_list)).
Georgy
Bagaimana dengan def f(x): return x + offset(yaitu, fungsi sederhana yang didefinisikan pada satu baris)? Setidaknya dengan flake8 saya tidak mendapatkan keluhan tentang baris kosong.
DocOc
1
@Julian Dalam beberapa kasus, Anda dapat menggunakan pemahaman bersarang:a, b = [[x + offset for x lst] for lst in (simple_list, another_simple_list)]
wjandrea
24

Lattyware benar sekali: Pada dasarnya PEP-8 ingin Anda menghindari hal-hal seperti

f = lambda x: 2 * x

dan sebaliknya gunakan

def f(x):
    return 2 * x

Namun, sebagaimana dialamatkan dalam laporan bug terbaru (Agustus 2014), pernyataan seperti berikut ini sekarang sesuai:

a.f = lambda x: 2 * x
a["f"] = lambda x: 2 * x

Karena pemeriksa PEP-8 saya belum mengimplementasikan ini dengan benar, saya mematikan E731 untuk saat ini.

Elmar Peise
sumber
8
Bahkan ketika menggunakan def, penguji PEP8 mengeluh E301 expected 1 blank line, found 0, jadi Anda kemudian harus menambahkan baris kosong yang jelek sebelum itu.
Adam Spires
1

Saya juga menghadapi situasi di mana bahkan tidak mungkin untuk menggunakan fungsi def (ined).

class SomeClass(object):
  # pep-8 does not allow this
  f = lambda x: x + 1  # NOQA

  def not_reachable(self, x):
    return x + 1

  @staticmethod
  def also_not_reachable(x):
    return x + 1

  @classmethod
  def also_not_reachable(cls, x):
    return x + 1

  some_mapping = {
      'object1': {'name': "Object 1", 'func': f},
      'object2': {'name': "Object 2", 'func': some_other_func},
  }

Dalam hal ini, saya benar-benar ingin membuat pemetaan milik kelas. Beberapa objek dalam pemetaan membutuhkan fungsi yang sama. Adalah tidak masuk akal untuk menempatkan fungsi bernama di luar kelas. Saya belum menemukan cara untuk merujuk ke metode (staticmethod, classmethod atau normal) dari dalam tubuh kelas. SomeClass belum ada saat kode dijalankan. Jadi merujuknya dari kelas juga tidak mungkin.

SIMP
sumber
Anda dapat merujuk also_not_reachablepada definisi pemetaan sebagaiSomeClass.also_not_reachable
yaccz
1
Saya tidak tahu poin apa yang ingin Anda sampaikan di sini. Setiap nama fungsi Anda dapat dijangkau seperti fdi 2,7 dan 3,5 bagi saya
Eric
Tidak, semua fungsi, kecuali fungsi lambda, tidak dapat dijangkau dari dalam tubuh Class. Anda akan mendapatkan AttributeError: ketik objek 'SomeClass' tidak memiliki atribut '...' jika Anda mencoba mengakses salah satu fungsi tersebut di objek some_mapping.
simP
3
@simP semuanya dapat diakses dengan sempurna. Yang dengan @staticmethoddan @classmethodtidak membutuhkan objek, hanya SomeClass.also_not_reachable(meskipun mereka membutuhkan nama yang berbeda). Jika Anda perlu mengaksesnya dari metode kelas, gunakan sajaself.also_not_reachable
ababak
@simP mungkin Anda harus mengganti nama *not_reachablemetode Anda sebagai not_as_easily_reachable_from_class_definition_as_a_lambdaxD
Romain Vincent