条件値での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

ifステートメントのある行に関する次のメッセージでPylintに警告されました。

[pylint] C1801:len(SEQUENCE)条件値として使用しない

ルールC1801は、一見したところ、あまり合理的に聞こえませんでした。リファレンスガイドの定義では、これが問題である理由が説明されていません。実際、それは間違いなくそれを不正使用と呼んでいます

len-as-condition(C1801)条件値として使用しないlen(SEQUENCE)Pylintが条件内でlen(sequence)の誤った使用を検出したときに使用されます。

私の検索の試みも、私にもっと深い説明を提供することに失敗しました。シーケンスの長さプロパティは遅延評価される可能性があり、__len__副作用を持つようにプログラムできることは理解していますが、それだけでPylintがそのような使用を誤って呼び出すのに十分な問題があるかどうかは疑問です。したがって、ルールを無視するようにプロジェクトを構成する前に、推論に何か欠けているかどうかを知りたいのです。

len(SEQ)条件値としての使用が問題になるのはいつですか?PylintがC1801で回避しようとしている主な状況は何ですか?


9
シーケンスの真実性を直接評価できるからです。pylintは、あなたがやりたいif files:if not files:
パトリック・ハウ

38
lenそれが呼び出されたコンテキストがわからないため、長さの計算がシーケンス全体のトラバースを意味する場合は、それが必要です。結果が単に0と比較されているかどうかはわかりません。ブール値の計算は、シーケンスが実際にどのくらい長いかに関係なく、最初の要素が表示された後に停止する場合があります。ここでは、パイリントは少し意見が分かれていると思います。を使用するのが間違っている状況は考えられませんlen。代わりの方法よりも悪いオプションであるだけです。
chepner 2017年

2
@ E_net4 PEP-8はおそらく最初の出発点だと思います。
Patrick Haugh 2017年


6
シーケンスには、C ++ imoのように 'empty()'または 'isempty()'が必要です。
JDonner 2018

回答:


281

len(SEQ)条件値としての使用が問題になるのはいつですか?PylintがC1801で回避しようとしている主な状況は何ですか?

そうではありません、実際に使用することは問題len(SEQUENCE)、それは効率的とはならないかもしれませんが(参照- chepnerさんのコメントを)。かかわらず、遵守のためのPylintチェックコードPEP 8スタイルガイドは、と述べています

シーケンス(文字列、リスト、タプル)の場合、空のシーケンスはfalseであるという事実を使用します。

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 2017

13
私は時折Pythonユーザーでもあり、「Pythonの方法」が独自の曖昧さの中に巻き込まれたような印象をしばしば持っています。
luqo33 2017年

3
一般的な質問ですが、これらのPEPの推奨事項は変更できますか?len(s) == 0が私の意見で優れているもう1つの理由は、他のタイプのシーケンスに対して一般化できることです。たとえば、pandas.Seriesnumpy配列。if not s:一方、はありません。その場合、可能なすべてのタイプの配列のようなオブジェクト(つまりpd.DataFrame.empty)に対して個別の評価を使用する必要があります。
火曜日

2
ちなみに、どのof collections.abcクラスも__bool__メソッドを述べていません。言い換えれば、bool(seq)それがaであることがわかっている場合、どうすれば使用できると確信できますかcollections.abc.Collection?さらに、一部のライブラリでbool(collection)は、クラスをチェックすることは禁止されています。
Eir Nym 2018年

42

NumPy配列を使用する場合は、実際にはseqのbool値をチェックするのではなく、len(seq)を使用する必要があることに注意してください。

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 2018

1

これはpylintの問題であり、len(x) == 0不正解とは見なされなくなりました。

ベア len(x)は条件として使用しないでください。of len(x)などの明示的な値と比較することは完全に問題なく、PEP 8では禁止されていません。if len(x) == 0if len(x) > 0

PEP 8から:

# Correct:
if not seq:
if seq:

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

長さ明示的にテストすることは禁止されていないことに注意してください。パイソンの禅の状態:

明示的は暗黙的よりも優れています。

間の選択では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ます。:私は、この場合のlintは、次のとあなたがif文を置き換えるために期待されることを信じてif not dirnames and not filenames:
E_net4がdownvoteで

__bool__関数が基になるシーケンスで定義されていない場合、真実性をテストすると、「常に真」であるという意図しない結果が生じます。
Erik Aronesty
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.