ทำไมการใช้ len (SEQUENCE) ในสภาพที่มีการพิจารณาว่าไม่ถูกต้องโดย Pylint


211

พิจารณาตัวอย่างโค้ดนี้:

from os import walk

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

ฉันตื่นตระหนกโดย Pylint กับข้อความนี้เกี่ยวกับบรรทัดที่มีคำสั่ง if:

[pylint] C1801: อย่าใช้len(SEQUENCE)เป็นค่าเงื่อนไข

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

len-as-condition (C1801) : ห้ามใช้len(SEQUENCE)เป็นค่าเงื่อนไขใช้เมื่อ Pylint ตรวจพบการใช้ len (ลำดับ) ที่ไม่ถูกต้องภายในเงื่อนไข

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

การใช้len(SEQ)เป็นค่าเงื่อนไขเป็นปัญหาเมื่อใด สถานการณ์ใดที่สำคัญที่ Pylint พยายามหลีกเลี่ยงด้วย C1801?


9
เพราะคุณสามารถประเมินความจริงของลำดับโดยตรง pylint ต้องการให้คุณทำif files:หรือif not files:
Patrick Haugh

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

2
@ E_net4 ฉันคิดว่าPEP-8น่าจะเป็นที่สำหรับเริ่มต้น
Patrick Haugh


6
ลำดับต้องมี 'empty ()' หรือ 'isempty ()' เช่น C ++ imo
JDonner

คำตอบ:


281

การใช้len(SEQ)เป็นค่าเงื่อนไขเป็นปัญหาเมื่อใด สถานการณ์ใดที่สำคัญที่ Pylint พยายามหลีกเลี่ยงด้วย C1801?

มันไม่ได้จริงๆมีปัญหากับการใช้งานlen(SEQUENCE)- แม้ว่ามันอาจจะไม่เป็นที่มีประสิทธิภาพ (ดูความคิดเห็นของ chepner ) โดยไม่คำนึงถึง Pylint ตรวจสอบรหัสเพื่อให้สอดคล้องกับคู่มือสไตล์ PEP 8ซึ่งระบุว่า

สำหรับซีเควนซ์ (สตริง, รายการ, สิ่งอันดับ) ให้ใช้ความจริงที่ว่าลำดับที่ว่างเปล่าเป็นเท็จ

Yes: if not seq:
     if seq:

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

ในฐานะที่เป็นโปรแกรมเมอร์ Python เป็นครั้งคราวซึ่งพูดภาษาต่างประเทศฉันคิดว่าlen(SEQUENCE)โครงสร้างนั้นสามารถอ่านได้และชัดเจนยิ่งขึ้น (“ ชัดเจนกว่าโดยปริยาย”) อย่างไรก็ตามการใช้ความจริงที่ว่าลำดับที่ว่างเปล่าประเมินFalseในบริบทบูลีนถือว่าเป็น "Pythonic" มากกว่า


วิธีการทำงานนี้แล้ว:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
Marichyasana

@Marichyasana ฉันเดาว่าสิ่งที่สามารถ (ในทางทฤษฎี) สามารถเขียนเป็นif next(iter(...), None) is not None:(ถ้าลำดับไม่สามารถมีNone) นั่นยาว แต่len(fnmatch...)ก็ยาวเกินไป ทั้งสองต้องแยก
Kirill Bulygin

13
ฉันยังเป็นผู้ใช้ Python เป็นครั้งคราวและบ่อยครั้งที่ฉันรู้สึกว่า "วิธี Pythonic" มีความยุ่งเหยิงในความคลุมเครือของตัวเอง
luqo33

3
เพียงแค่คำถามทั่วไป PEP เหล่านี้สามารถแก้ไขได้หรือไม่? อีกเหตุผลหนึ่งที่ว่าทำไมสิ่งที่len(s) == 0เหนือกว่าในความคิดของฉันก็คือมันเป็น generalizable สำหรับลำดับอื่น ๆ ตัวอย่างเช่นpandas.Seriesและอาร์เรย์ที่ไม่แน่นอน if not s:ไม่ได้อยู่ในมือของคุณและในกรณีนั้นคุณจะต้องใช้การประเมินแยกต่างหากสำหรับวัตถุที่มีลักษณะคล้ายอาร์เรย์ทุกประเภท (เช่นpd.DataFrame.empty)
Marses

2
โดยวิธีการof collections.abcเรียนไม่มี__bool__วิธีการที่ระบุ ในคำอื่น ๆ วิธีการที่ฉันสามารถมั่นใจได้ว่าจะสามารถใช้bool(seq)ถ้าฉันรู้ว่ามันเป็นcollections.abc.Collection? มอเรโซห้องสมุดบางแห่งประกาศว่าห้ามมิให้ตรวจสอบbool(collection)คลาสของพวกเขา
Eir Nym

42

โปรดทราบว่าการใช้ len (seq) นั้นจำเป็นต้องใช้จริง (แทนที่จะตรวจสอบค่าบูลของ seq) เมื่อใช้อาร์เรย์ NumPy

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

ส่งผลให้เกิดข้อยกเว้น: ValueError: ค่าความจริงของอาร์เรย์ที่มีองค์ประกอบมากกว่าหนึ่งองค์ประกอบนั้นไม่ชัดเจน ใช้ a.any () หรือ a.all ()

และด้วยเหตุนี้สำหรับรหัสที่ใช้ทั้งรายการ Python และอาร์เรย์ NumPy ข้อความ C1801 จึงมีประโยชน์น้อยกว่า


5
ฉันเห็นด้วยกับคำสั่งของคุณ ด้วยการเพิ่ม# 1405ตอนนี้ฉันหวังว่าจะเห็น C1801 เปลี่ยนเป็นสิ่งที่มีประโยชน์หรือถูกปิดใช้งานโดยค่าเริ่มต้น
E_net4 ตั้งค่าสถานะให้ความร้อนใน

2
รวมทั้งมันไม่มีประโยชน์สำหรับการตรวจสอบว่าลำดับมีจำนวนองค์ประกอบที่กำหนดหรือไม่ มันดีสำหรับการตรวจสอบเท่านั้นว่ามันว่างเปล่าในกรณีที่ดีที่สุด
PabTorre

1

นี่เป็นปัญหาที่เกิดขึ้นในไพลินต์และมันก็ไม่ได้พิจารณาlen(x) == 0ว่าไม่ถูกต้องอีกต่อไป

คุณไม่ควรใช้ผ้าเปลือย len(x)เป็นเงื่อนไข เปรียบเทียบlen(x)กับค่าอย่างชัดเจนเช่นif len(x) == 0การif len(x) > 0มีทั้งหมดดีและไม่ต้องห้ามตาม PEP 8

จากPEP 8 :

# Correct:
if not seq:
if seq:

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

โปรดทราบว่าอย่างชัดเจนการทดสอบสำหรับความยาวไม่ได้ห้าม The Zen of Pythonกล่าวว่า:

ชัดเจนดีกว่าโดยปริยาย

ในการเลือกระหว่างif not seqและif not len(seq)ทั้งสองมีความหมาย แต่พฤติกรรมนั้นแตกต่างกัน แต่if len(seq) == 0หรือif len(seq) > 0เป็นการเปรียบเทียบที่ชัดเจนและในหลายบริบทพฤติกรรมที่ถูกต้อง

ใน pylint, PR 2815ได้รับการแก้ไขข้อผิดพลาดนี้เป็นครั้งแรกที่รายงานปัญหา 2684 มันจะยังคงบ่นเกี่ยวกับแต่มันจะไม่บ่นเกี่ยวกับif len(seq) if len(seq) > 0การประชาสัมพันธ์ถูกผสานระหว่าง 2019-03-19 ดังนั้นถ้าคุณใช้ pylint 2.4 (เผยแพร่ 2019-09-14) คุณไม่ควรเห็นปัญหานี้


0

ไพลินท์ล้มเหลวในการใช้รหัสของฉันและการวิจัยทำให้ฉันไปที่โพสต์นี้:

../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)

นี่คือรหัสของฉันก่อนหน้า:

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

นี่คือหลังจากแก้ไขรหัสของฉัน โดยการใช้int() attributeฉันดูเหมือนจะมีความพึงพอใจ Pep8 / Pylint และดูเหมือนจะไม่ส่งผลเสียต่อรหัสของฉัน:

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

My Fix

.__trunc__()ดูเหมือนว่าจะมีความจำเป็นที่จะต้องเพิ่มลำดับ

ฉันไม่เห็นความแตกต่างในพฤติกรรม แต่ถ้าใครรู้เฉพาะที่ฉันหายไปโปรดแจ้งให้เราทราบ


1
คุณกำลังเรียก__trunc__()ผลลัพธ์len(seq)ที่ซึ่ง (ค่อนข้างซ้ำซ้อน) ตัดทอนค่าความยาวเป็นจำนวนเต็ม มีเพียง "feints" ผ้าสำลีโดยไม่ต้องพูดถึงเหตุผลที่อยู่เบื้องหลัง คำแนะนำในคำตอบที่ยอมรับนั้นใช่สำหรับคุณหรือไม่
E_net4 ตั้งค่าสถานะให้ความร้อน

ไม่ใช่ในความพยายามของฉัน ฉันเข้าใจความซ้ำซ้อน แต่แม้หลังจากผู้พัฒนาได้แก้ไขปัญหานี้แล้วในgithub.com/PyCQA/pylint/issues/1405 & 2684 และได้รับการรวมเข้าด้วยกันเพื่อให้เข้าใจว่านี่ไม่ควรเป็นปัญหาเมื่อเรียกใช้ pylint แต่ ฉันยังคงเห็นปัญหานี้แม้หลังจากอัปเดตไพลินต์ของฉัน ฉันแค่อยากจะแบ่งปันราวกับthis worked for meว่ามันไม่เหมาะสมทั้งหมด แต่เพื่อให้ความกระจ่างแม้ว่าจะซ้ำซ้อนหากคุณกำลังทำ len (seq) == 0 การเปรียบเทียบtruncไม่ควรทำอะไรเลยเพราะมันเป็นจำนวนเต็มอยู่แล้ว ขวา?
JayRizzo

1
แน่นอนว่ามันเป็นจำนวนเต็มและ__trunc__()ไม่ทำอะไรที่มีความหมาย โปรดทราบว่าฉันไม่ได้อ้างถึงการเปรียบเทียบว่าซ้ำซ้อน แต่เป็นการพยายามตัดทอนความยาวนี้ คำเตือนจะหายไปเพียงเพราะคาดว่าจะแสดงออกในแบบฟอร์มlen(seq) == 0เท่านั้น ฉันเชื่อว่าผ้าสำลีในกรณีนี้คาดว่าคุณจะแทนที่คำสั่ง if ด้วยสิ่งต่อไปนี้:if not dirnames and not filenames:
E_net4 ตั้งค่าสถานะ heat

การทดสอบความจริงมีผลที่ตามมาโดยไม่ตั้งใจของการเป็น "เสมอจริง" ถ้า__bool__ฟังก์ชั่นไม่ได้กำหนดไว้ในลำดับพื้นฐาน
Erik Aronesty
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.