为什么Pylint认为在条件值中使用len(SEQUENCE)不正确?


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(sequence)时使用。

我的搜索尝试也未能为我提供更深入的解释。我确实知道,序列的length属性可能会被延迟评估,并且__len__可以编程为具有副作用,但是令人怀疑的是,仅此一个问题是否足以使Pylint认为这种用法不正确。因此,在我简单地将项目配置为忽略规则之前,我想知道我的推理中是否缺少某些内容。

什么时候将len(SEQ)用作条件值有问题?Pylint尝试使用C1801避免哪些主要情况?


9
因为您可以直接评估序列的真实性。pylint希望您这样做if files:if not files:
Patrick Haugh

38
len不知道调用它的上下文,因此如果计算长度意味着遍历整个序列,则必须;它不知道将结果与0进行比较。计算布尔值可以在看到第一个元素后停止,而不管该序列实际有多长时间。不过,我认为pylint在这方面有点自以为是;我无法想到使用错误的任何情况len,只是这是比其他选择更糟糕的选择。
chepner

2
@ E_net4我认为PEP-8可能是开始的地方。
Patrick Haugh


6
序列需要像C ++ imo一样的'empty()'或'isempty()'。
JDonner '18

Answers:


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的用户,并且经常给人一种印象,即“ Python方式”在其自身的歧义中有些纠结。
luqo33

3
只是一个普遍的问题,这些PEP建议是否可以修订?len(s) == 0在我看来,之所以优越的另一个原因是,它可用于其他类型的序列。例如,pandas.Series和numpy数组。if not s:另一方面,它不是,并且在这种情况下,您将需要对类似于数组的所有可能类型的对象(即pd.DataFrame.empty)使用单独的评估。
Marses

2
顺便说一句,没有of collections.abc类说明__bool__方法。换句话说,bool(seq)如果我知道这是一个数字,如何确定可以使用collections.abc.Collection?此外,一些库拒绝接受检查bool(collection)其类的声明。
Eir Nym,

42

请注意,使用NumPy数组时,实际上需要使用len(seq)(而不是仅检查seq的bool值)。

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

这是pylint中的问题,并且不再视为len(x) == 0不正确。

您不应以裸露 len(x)为条件。比较len(x)反对一个明确的值,如if len(x) == 0if len(x) > 0是PEP 8完全正常和不禁止。

PEP 8

# Correct:
if not seq:
if seq:

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

请注意,不禁止明确测试长度Python禅宗指出:

显式胜于隐式。

在这两者之间的选择if not seqif not len(seq),无一不是隐含的,而行为是不同的。但是if len(seq) == 0或者if len(seq) > 0是显式比较,并且在许多情况下是正确的行为。

在pylint中,PR 2815修复了此错误,该错误首先报告为问题2684。它会继续抱怨if len(seq),但不再抱怨if len(seq) > 0。PR已在2019-03-19合并,因此如果您使用的是pylint 2.4(于2019-09-14发布),则不应看到此问题。


0

Pylint未能提供我的代码,研究使我转向了这篇文章:

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

我的修复

通过增加.__trunc__()顺序,似乎已经解决了需求。

我的行为没有区别,但是如果有人知道我所缺少的细节,请告诉我。


1
您正在调用__trunc__()的输出len(seq),该输出(有点多余)将长度值截断为整数。它仅“伪装”皮棉,而没有解决其背后的原因。被接受的答案中的建议对您有用吗?
E_net4是

不是我的尝试。我了解冗余,但是即使开发人员已在github.com/PyCQA/pylint/issues/1405&2684中解决了此问题,并且已将其合并,据我了解,运行pylint时这应该不是问题,但是即使更新了pylint,我仍然看到此问题。我只是想分享为this worked for me,即使这并不完全合适。但是,为了弄清楚即使进行len(seq)== 0比较也是多余的,trunc不必做任何事情,因为它们已经是整数。对?
JayRizzo

1
确实,它已经是一个整数,并且__trunc__()没有任何有意义的作用。请注意,我并不是说比较是多余的,而是这种试图缩短长度的尝试。该警告仅会消失,因为它只希望该形式的表达len(seq) == 0。我相信,在这种情况下,皮棉会希望您将if语句替换为以下内容:if not dirnames and not filenames:
E_net4是一张赞词,

如果__bool__未在基础序列中定义功能,则进行真实性测试会带来意想不到的后果,即“始终为真” 。
Erik Aronesty '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.