Почему использование 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] C1801: не использовать в len(SEQUENCE)качестве значения условия

Правило C1801, на первый взгляд, показалось мне не очень разумным, и определение в справочном руководстве не объясняет, почему это проблема. Фактически, это прямо называет это неправильным использованием .

len-as-condition (C1801) : Не использовать в len(SEQUENCE)качестве значения условия. Используется, когда Pylint обнаруживает неправильное использование len (последовательности) внутри условий.

Мои попытки поиска также не дали мне более глубокого объяснения. Я понимаю, что свойство длины последовательности может быть лениво оценено, и это __len__может быть запрограммировано, чтобы иметь побочные эффекты, но сомнительно, является ли это само по себе достаточно проблематичным для Pylint, чтобы назвать такое использование неправильным. Следовательно, прежде чем я просто настрою свой проект на игнорирование правила, я хотел бы знать, что я что-то упускаю в своих рассуждениях.

Когда использование len(SEQ)в качестве значения условия проблематично? Каких основных ситуаций Pylint пытается избежать с помощью C1801?


9
Потому что вы можете оценить правдивость последовательности напрямую. Пилинт хочет, чтобы вы делали if files:илиif not files:
— Патрик

38
lenне знает контекст, в котором он вызывается, поэтому, если вычисление длины означает пересечение всей последовательности, оно должно; он не знает, что результат просто сравнивается с 0. Вычисление логического значения может прекратиться после того, как он увидит первый элемент, независимо от того, какова длина последовательности на самом деле. Я думаю, что Пилинт здесь немного самоуверен; Я не могу придумать ни одной ситуации, когда это неправильно использовать len, просто что это худший вариант, чем альтернатива.
— Чепнер

2
@ E_net4 Я думаю, что PEP-8 - это, вероятно, место для старта.
— Патрик


6
Для SEQUENCES требуется 'empty ()' или 'isempty ()', как в C ++ imo.
— ДжонДоннер

Ответы:


281

Когда использование len(SEQ)в качестве значения условия проблематично? Каких основных ситуаций Pylint пытается избежать с помощью C1801?

Это не очень проблематично в использовании len(SEQUENCE)- хотя это может быть не так эффективно (см . Комментарий Чепнера ). В любом случае, Pylint проверяет код на соответствие руководству по стилю PEP 8, в котором говорится, что

Для последовательностей (строк, списков, кортежей) используйте тот факт, что пустые последовательности являются ложными.

Yes: if not seq:
     if seq:

No:  if len(seq):
     if not len(seq):

Будучи случайным программистом на Python, который переключается между языками, я считаю len(SEQUENCE)конструкцию более читаемой и явной («Явное лучше, чем неявное»). Однако использование того факта, что пустая последовательность оценивается Falseв логическом контексте, считается более «питоническим».


Как сделать эту работу тогда:if len(fnmatch.filter(os.listdir(os.getcwd()), 'f_*')):
— Маричьясана

@Marichyasana Я думаю, что подобные вещи можно (теоретически) записать как if next(iter(...), None) is not None:(если последовательность не может содержать None). Это долго, но len(fnmatch...)слишком долго; оба должны быть разделены.
— Кирилл Булыгин

13
Я также временный пользователь Python, и часто у меня складывается впечатление, что «Pythonic way» запутался в своей собственной двусмысленности.
— Luqo33

3
Просто общий вопрос, можно ли пересмотреть эти рекомендации ПКП? Другая причина, по которой, на 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)их классы.
— Эйр Ним

42

Обратите внимание, что на самом деле требуется использование len (seq) (вместо простой проверки значения bool seq) при использовании массивов NumPy.

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) == 0of if len(x) > 0, совершенно нормально и не запрещено PEP 8.

Из ППК 8 :

# Correct:
if not seq:
if seq:

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

Обратите внимание, что явное тестирование длины не запрещено. Дзен Python гласит:

Явное лучше, чем неявное.

В выборе между if not seqи if 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, но Я все еще вижу эту проблему даже после обновления моего пилынта. Я просто хотел поделиться, как this worked for me, даже если это не совсем уместно. Но, чтобы уточнить, даже если это избыточно, если вы выполняете сравнение len (seq) == 0, trunc не должен ничего делать, так как он уже является целым числом. право?
— JayRizzo

1
Точно, это уже целое число, и __trunc__()не делает ничего значащего. Обратите внимание, что я ссылался не на сравнение как на избыточное, а на попытку урезания длины. Предупреждение исчезает только потому, что ожидает только выражение формы len(seq) == 0. Я полагаю, что в этом случае ожидается, что вы замените оператор if следующим:if not dirnames and not filenames:
— E_net4 бригады

Проверка на правдивость имеет непреднамеренные последствия того, что она «всегда верна», если __bool__функция не определена в основной последовательности.
— Эрик Аронесты
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.