ChatGPT解决这个技术问题 Extra ChatGPT

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

考虑到这个代码片段:

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 对我来说听起来不是很合理,definition on the reference guide 并没有解释为什么这是一个问题。事实上,它完全称其为错误使用

len-as-condition (C1801):不要使用 len(SEQUENCE) 作为条件值 当 Pylint 检测到错误使用 len(sequence) 内部条件时使用。

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

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

因为您可以直接评估序列的真实性。 pylint 想让你做 if files:if not files:
len 不知道调用它的上下文,所以如果计算长度意味着遍历整个序列,它必须;它不知道结果只是与 0 进行比较。计算布尔值可以在它看到第一个元素后停止,无论序列实际有多长。不过,我认为 pylint 在这里有点固执己见。我想不出使用 len错误的任何情况,只是它是比替代方案更糟糕的选择。
@E_net4 我认为 PEP-8 可能是开始的地方。
序列需要像 C++ imo 一样的 'empty()' 或 'isempty()'。

A
Anthony Geoghegan

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

使用 len(SEQUENCE) 并不是真正有问题 - 尽管它可能没有那么高效(请参阅 chepner’s comment)。无论如何,Pylint 都会检查代码是否符合 PEP 8 style guide,其中指出

对于序列(字符串、列表、元组),使用空序列为假的事实。是:如果不是 seq:如果 seq:否:如果 len(seq):如果不是 len(seq):

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


那么如何使这项工作:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
@Marichyasana 我想这样的事情(理论上)可以写成 if next(iter(...), None) is not None: (如果序列不能包含 None)。这很长,但 len(fnmatch...) 也很长;两者都需要拆分。
我也是一个偶尔使用 Python 的用户,我常常觉得“Pythonic 方式”有点纠结于它自己的模棱两可。
只是一个一般性问题,这些 PEP 建议可以修改吗?我认为 len(s) == 0 优越的另一个原因是它可以推广到其他类型的序列。例如,pandas.Series 和 numpy 数组。另一方面,if not s: 不是,在这种情况下,您需要对所有可能类型的类似数组的对象(即 pd.DataFrame.empty)使用单独的评估。
顺便说一句,没有 of collections.abc 类声明 __bool__ 方法。换句话说,如果我知道它是 collections.abc.Collection,我怎么能确定我可以使用它?此外,一些库禁止检查 bool(collection) 的类。
C
Cameron Hayne

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


我同意你的说法。现在提出了 issue #1405,我希望看到 C1801 要么改造成有用的东西,要么默认禁用。
另外,它对于检查序列是否具有给定数量的元素是无用的。仅用于检查它在最好的情况下是完全空的。
P
Peter Mortensen

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

您不应使用 bare len(x) 作为条件。将 len(x) 与显式值(例如 if len(x) > 0if len(x) == 0)进行比较是完全可以的,并且不受 PEP 8 的禁止。

PEP 8

# 正确: if not seq: if seq: # 错误: if len(seq): if not len(seq):

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

显式优于隐式。

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

在 Pylint 中,PR 2815 已修复此错误,最初报告为 issue 2684。它会继续抱怨 if len(seq),但不会再抱怨 if len(seq) > 0。 PR 已于 2019 年 3 月 19 日合并,因此如果您使用的是 Pylint 2.4(2019 年 9 月 14 日发布),您应该不会看到此问题。


J
JayRizzo

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__() 添加到序列中,它似乎满足了需求。

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


您在 len(seq) 的输出上调用 __trunc__(),这(有点多余)将长度值截断为整数。它只是“假装”了 lint,而没有解决其背后的原因。接受的答案中的建议对您不起作用吗?
不在我的尝试中。我理解冗余,但即使在 github.com/PyCQA/pylint/issues/1405 & 中的开发人员解决了这个问题之后2684 并已合并,据我了解,这在运行 pylint 时应该不是问题,但即使在更新我的 pylint 后我仍然看到这个问题。我只是想以 this worked for me 的身份分享,即使它并不完全合适。但是,为了澄清,即使你在进行 len(seq) == 0 比较是多余的,trunc 也不应该做任何事情,因为它们已经是整数。正确的?
确切地说,它已经是一个整数,而 __trunc__() 没有做任何有意义的事情。请注意,我并没有将比较称为冗余,而是将这种尝试截断长度。警告只会消失,因为它只需要 len(seq) == 0 形式的表达式。我相信这种情况下的 lint 会期望您将 if 语句替换为以下内容:if not dirnames and not filenames:
如果未在基础序列中定义 __bool__ 函数,则测试真实性会产生“始终为真”的意外后果。

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅