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

Why is the use of len(SEQUENCE) in condition values considered incorrect by Pylint?

考虑这个代码片段:

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:Do not use len(SEQUENCE) as condition value

C1801 规则,乍一看,我觉得不太合理,definition on the reference guide 也没有解释为什么这是个问题。事实上,它完全称其为错误使用

len-as-condition (C1801): Do not use len(SEQUENCE) as condition value Used when Pylint detects incorrect use of len(sequence) inside conditions.

我的搜索尝试也未能为我提供更深入的解释。我确实理解序列的长度 属性 可能会被懒惰地评估,并且 __len__ 可以被编程为有副作用,但值得怀疑的是,仅凭这一点是否足以让 Pylint 称这种使用不正确.因此,在我简单地配置我的项目以忽略该规则之前,我想知道我的推理是否遗漏了什么。

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

When is the use of len(SEQ) as a condition value problematic? What major situations is Pylint attempting to avoid with C1801?

使用 len(SEQUENCE) 确实 没有问题——尽管它可能效率不高(参见 ). Regardless, Pylint checks code for compliance with the PEP 8 style guide 指出

For sequences, (strings, lists, tuples), use the fact that empty sequences are false.

<b>Yes:</b> if not seq:
     if seq:

<b>No:</b>  if len(seq):
     if not len(seq):

作为偶尔在不同语言之间穿梭的 Python 程序员,我认为 len(SEQUENCE) 结构更具可读性和明确性(“明确优于隐含”)。但是,使用空序列在布尔上下文中计算为 False 这一事实被认为更“Pythonic”。

请注意,在使用 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 消息的帮助不大。

Pylint 因我的代码而失败,研究使我想到了这个 post:

../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__() 似乎已经满足了需求。

我看不出行为有什么不同,但如果有人知道我遗漏的细节,请告诉我。

这是 Pylint 中的一个问题,它不再认为 len(x) == 0 不正确。

您不应使用 bare 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):

请注意,不禁止显式测试长度Zen of Python 状态:

Explicit is better than implicit.

if not seqif not len(seq)之间的选择中,两者都是隐含的,但行为不同。但是 if len(seq) == 0if len(seq) > 0 是明确的比较,在许多情况下都是正确的行为。

在 Pylint 中,PR 2815 has fixed this bug, first reported as issue 2684。它会继续抱怨if len(seq),但不会再抱怨if len(seq) > 0。 PR 于 2019-03-19 合并,因此如果您使用的是 Pylint 2.4(2019-09-14 发布),您应该不会看到此问题。