Почему использование 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?

E_net4 нижней бригады
источник
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__()к последовательности, кажется, что она исчерпала потребность.

Я не вижу различий в поведении, но если кто-то знает особенности, которые мне не хватает, пожалуйста, дайте мне знать.

JayRizzo
источник
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__функция не определена в основной последовательности.
Эрик Аронесты