考虑到这个代码片段:
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 避免哪些主要情况?
if files:
或 if not files:
len
不知道调用它的上下文,所以如果计算长度意味着遍历整个序列,它必须;它不知道结果只是与 0 进行比较。计算布尔值可以在它看到第一个元素后停止,无论序列实际有多长。不过,我认为 pylint 在这里有点固执己见。我想不出使用 len
是错误的任何情况,只是它是比替代方案更糟糕的选择。
何时使用 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”。
请注意,在使用 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 中的一个问题,它不再认为 len(x) == 0
不正确。
您不应使用 bare len(x)
作为条件。将 len(x)
与显式值(例如 if len(x) > 0
的 if len(x) == 0
)进行比较是完全可以的,并且不受 PEP 8 的禁止。
从 PEP 8:
# 正确: if not seq: if seq: # 错误: if len(seq): if not len(seq):
请注意,不禁止明确测试长度。 Zen of Python 指出:
显式优于隐式。
在 if not seq
和 if not len(seq)
之间的选择中,两者都是隐式的,但行为不同。但是 if len(seq) == 0
或 if 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 日发布),您应该不会看到此问题。
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,而没有解决其背后的原因。接受的答案中的建议对您不起作用吗?
this worked for me
的身份分享,即使它并不完全合适。但是,为了澄清,即使你在进行 len(seq) == 0 比较是多余的,trunc 也不应该做任何事情,因为它们已经是整数。正确的?
__trunc__()
没有做任何有意义的事情。请注意,我并没有将比较称为冗余,而是将这种尝试截断长度。警告只会消失,因为它只需要 len(seq) == 0
形式的表达式。我相信这种情况下的 lint 会期望您将 if 语句替换为以下内容:if not dirnames and not filenames:
__bool__
函数,则测试真实性会产生“始终为真”的意外后果。
if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
if next(iter(...), None) is not None:
(如果序列不能包含None
)。这很长,但len(fnmatch...)
也很长;两者都需要拆分。len(s) == 0
优越的另一个原因是它可以推广到其他类型的序列。例如,pandas.Series
和 numpy 数组。另一方面,if not s:
不是,在这种情况下,您需要对所有可能类型的类似数组的对象(即pd.DataFrame.empty
)使用单独的评估。of collections.abc
类声明__bool__
方法。换句话说,如果我知道它是collections.abc.Collection
,我怎么能确定我可以使用它?此外,一些库禁止检查bool(collection)
的类。