Mengapa penggunaan len (SEQUENCE) dalam nilai kondisi dianggap salah oleh Pylint?

211

Mempertimbangkan cuplikan kode ini:

from os import walk

files = []
for (dirpath, _, filenames) in walk(mydir):
    # more code that modifies files
if len(files) == 0: # <-- C1801
    return None

Saya terkejut oleh Pylint dengan pesan ini mengenai baris dengan pernyataan if:

[pylint] C1801: Jangan gunakan len(SEQUENCE)sebagai nilai kondisi

Aturan C1801, pada pandangan pertama, tidak terdengar sangat masuk akal bagi saya, dan definisi pada panduan referensi tidak menjelaskan mengapa ini menjadi masalah. Bahkan, itu benar-benar menyebutnya penggunaan yang salah .

len-as-condition (C1801) : Jangan gunakan len(SEQUENCE)sebagai nilai kondisi Digunakan ketika Pylint mendeteksi penggunaan salah len (urutan) di dalam kondisi.

Upaya pencarian saya juga gagal memberi saya penjelasan yang lebih dalam. Saya mengerti bahwa properti panjang urutan dapat dievaluasi dengan malas, dan yang __len__dapat diprogram untuk memiliki efek samping, tetapi dipertanyakan apakah itu saja yang cukup bermasalah bagi Pylint untuk menyebut penggunaan seperti itu salah. Oleh karena itu, sebelum saya cukup mengkonfigurasi proyek saya untuk mengabaikan aturan, saya ingin tahu apakah saya kehilangan sesuatu dalam alasan saya.

Kapan penggunaan len(SEQ)sebagai nilai kondisi bermasalah? Situasi utama apa yang berusaha dihindari oleh Pylint dengan C1801?

E_net4 dari brigade downvote
sumber
9
Karena Anda dapat mengevaluasi kebenaran urutan secara langsung. pylint ingin Anda melakukan if files:atauif not files:
Patrick Haugh
38
lentidak tahu konteks di mana ia dipanggil, jadi jika menghitung panjang berarti melintasi seluruh urutan, ia harus; tidak tahu bahwa hasilnya hanya dibandingkan dengan 0. Menghitung nilai boolean dapat berhenti setelah melihat elemen pertama, terlepas dari berapa lama urutan sebenarnya. Saya pikir pylint sedang sedikit berpendapat di sini, meskipun; Saya tidak bisa memikirkan situasi di mana itu salah untuk digunakan len, hanya saja itu pilihan yang lebih buruk daripada alternatifnya.
chepner
2
@ E_net4 Saya pikir PEP-8 mungkin adalah tempat untuk memulai.
Patrick Haugh
6
URUTAN membutuhkan 'kosong ()' atau 'isempty ()' seperti C ++ imo.
JDonner

Jawaban:

281

Kapan penggunaan len(SEQ)sebagai nilai kondisi bermasalah? Situasi utama apa yang berusaha dihindari oleh Pylint dengan C1801?

Ini tidak benar - benar bermasalah untuk digunakan len(SEQUENCE)- meskipun mungkin tidak seefisien (lihat komentar chepner ). Apapun, Pylint memeriksa kode untuk kepatuhan dengan panduan gaya PEP 8 yang menyatakan itu

Untuk urutan, (string, daftar, tupel), gunakan fakta bahwa urutan kosong salah.

Yes: if not seq:
     if seq:

No:  if len(seq):
     if not len(seq):

Sebagai programmer Python sesekali, yang berpindah antar bahasa, saya akan menganggap len(SEQUENCE)konstruk lebih mudah dibaca dan eksplisit ("Eksplisit lebih baik daripada implisit"). Namun, menggunakan fakta bahwa urutan kosong mengevaluasi Falsedalam konteks Boolean dianggap lebih "Pythonic".

Anthony Geoghegan
sumber
Bagaimana cara membuatnya bekerja:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
Marichyasana
@Marichyasana Saya kira hal-hal seperti itu dapat (secara teoritis) ditulis sebagai if next(iter(...), None) is not None:(jika urutannya tidak mengandung None). Itu panjang, tapi len(fnmatch...)terlalu panjang; keduanya perlu dibagi.
Kirill Bulygin
13
Saya juga pengguna Python sesekali dan sering kali saya memiliki kesan bahwa "cara Pythonic" punya semacam kusut dalam ambiguitasnya sendiri.
luqo33
3
Hanya pertanyaan umum, dapatkah rekomendasi PEP ini direvisi? Alasan lain mengapa len(s) == 0itu lebih unggul menurut saya adalah bahwa itu dapat digeneralisasi untuk jenis urutan lainnya. Misalnya, pandas.Seriesdan array numpy. if not s:tidak di sisi lain, dan dalam hal ini Anda perlu menggunakan evaluasi terpisah untuk semua jenis objek seperti array yang mungkin (yaitu pd.DataFrame.empty).
Marses
2
By the way, tidak ada of collections.abckelas yang menyatakan __bool__metode. Dengan kata lain, bagaimana saya bisa yakin bahwa saya dapat menggunakan bool(seq)jika saya tahu itu adalah collections.abc.Collection? Selain itu, beberapa perpustakaan menyatakan bahwa dilarang memeriksa bool(collection)kelas mereka.
Eir Nym
42

Perhatikan bahwa penggunaan len (seq) sebenarnya diperlukan (alih-alih hanya memeriksa nilai bool dari seq) saat menggunakan array NumPy.

a = numpy.array(range(10))
if a:
    print "a is not empty"

menghasilkan pengecualian: ValueError: Nilai kebenaran array dengan lebih dari satu elemen ambigu. Gunakan a.any () atau a.all ()

Dan karenanya untuk kode yang menggunakan daftar Python dan array NumPy, pesan C1801 kurang membantu.

Cameron Hayne
sumber
5
Saya setuju dengan pernyataan Anda. Dengan masalah # 1405 sekarang diangkat, saya berharap melihat C1801 direformasi menjadi sesuatu yang bermanfaat atau dinonaktifkan secara default.
E_net4 dari downvote brigade
2
ditambah itu tidak berguna untuk memeriksa apakah suatu urutan memiliki sejumlah elemen. Ini hanya baik untuk memeriksa itu benar-benar kosong dalam kasus terbaik.
PabTorre
1

Ini merupakan masalah di pylint, dan itu tidak lagi dianggap len(x) == 0sebagai salah.

Anda tidak harus menggunakan telanjang len(x) sebagai syarat. Membandingkan len(x)terhadap nilai eksplisit, seperti if len(x) == 0dari if len(x) > 0benar-benar baik-baik saja dan tidak dilarang oleh PEP 8.

Dari PEP 8 :

# Correct:
if not seq:
if seq:

# Wrong:
if len(seq):
if not len(seq):

Perhatikan bahwa pengujian secara eksplisit untuk panjang tidak dilarang. The Zen of Python menyatakan:

Eksplisit lebih baik daripada implisit.

Dalam pilihan antara if not seqdan if not len(seq), keduanya implisit tetapi perilaku berbeda. Tetapi if len(seq) == 0atau if len(seq) > 0apakah perbandingan eksplisit dan dalam banyak konteks perilaku yang benar.

Dalam pylint, PR 2815 telah memperbaiki bug ini, pertama kali dilaporkan sebagai masalah 2684 . Itu akan terus mengeluh if len(seq), tetapi tidak akan lagi mengeluh if len(seq) > 0. PR digabung 2019-03-19 jadi jika Anda menggunakan pylint 2.4 (dirilis 2019-09-14) Anda seharusnya tidak melihat masalah ini.

gerrit
sumber
0

Pylint gagal untuk kode saya dan penelitian membawa saya ke pos ini:

../filename.py:49:11: C1801: Do not use `len(SEQUENCE)` to determine if a sequence is empty (len-as-condition)
../filename.py:49:34: C1801: Do not use `len(SEQUENCE)` to determine if a sequence is empty (len-as-condition)

Ini adalah kode saya sebelumnya:

def list_empty_folders(directory):
"""The Module Has Been Build to list empty Mac Folders."""
for (fullpath, dirnames, filenames) in os.walk(directory):
    if len(dirnames) == 0 and len(filenames) == 0:
        print("Exists: {} : Absolute Path: {}".format(
            os.path.exists(fullpath), os.path.abspath(fullpath)))

Ini setelah perbaikan kode saya. Dengan menggunakan int() attribute, saya tampaknya telah memenuhi Pep8 / Pylint dan tampaknya tidak memiliki dampak negatif pada kode saya:

def list_empty_folders(directory):
"""The Module Has Been Build to list empty Mac Folders."""
for (fullpath, dirnames, filenames) in os.walk(directory):
    if len(dirnames).__trunc__() == 0 and len(filenames).__trunc__() == 0:
        print("Exists: {} : Absolute Path: {}".format(
            os.path.exists(fullpath), os.path.abspath(fullpath)))

Perbaiki saya

Dengan menambahkan .__trunc__()urutan itu tampaknya telah memenuhi kebutuhan.

Saya tidak melihat perbedaan dalam perilaku, tetapi jika ada yang tahu secara spesifik bahwa saya hilang, tolong beri tahu saya.

JayRizzo
sumber
1
Anda memanggil __trunc__()output dari len(seq), yang (agak berlebihan) memotong nilai panjang ke integer. Itu hanya "membohongi" serat itu tanpa menyinggung alasan di baliknya. Tidakkah saran dalam jawaban yang diterima bekerja untuk Anda?
E_net4 dari downvote brigade
Tidak dalam usahaku. Saya memahami redundansi, tetapi bahkan setelah masalah ini telah diatasi oleh pengembang di github.com/PyCQA/pylint/issues/1405 & 2684 dan telah digabungkan, menurut pemahaman saya ini seharusnya tidak menjadi masalah saat menjalankan pylint tetapi Saya masih melihat masalah ini bahkan setelah memperbarui pylint saya. Saya hanya ingin berbagi, seolah this worked for me-olah itu tidak sepenuhnya sesuai. Tetapi, untuk memperjelas bahkan jika itu berlebihan jika Anda melakukan perbandingan len (seq) == 0, trunc seharusnya tidak perlu melakukan apa-apa karena mereka sudah bilangan bulat. Baik?
JayRizzo
1
Tepatnya, itu sudah bilangan bulat, dan __trunc__()tidak melakukan sesuatu yang berarti. Perhatikan bahwa saya tidak menyebut perbandingan sebagai berlebihan, tetapi untuk upaya memotong panjang. Peringatan hanya menghilang karena hanya mengharapkan ekspresi formulir len(seq) == 0. Saya percaya bahwa serat dalam hal ini akan mengharapkan Anda untuk mengganti pernyataan if dengan yang berikut:if not dirnames and not filenames:
E_net4 dari downvote brigade
Menguji kebenaran memiliki konsekuensi yang tidak diinginkan sebagai "selalu benar" jika __bool__fungsi tersebut tidak didefinisikan dalam urutan yang mendasarinya.
Erik Aronesty