Чому використання ліна (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

Мене тривожив Pylint цим повідомленням щодо рядка із заявою if:

[pylint] C1801: Не використовувати len(SEQUENCE)як значення умови

Правило C1801, на перший погляд, для мене не звучало дуже розумно, а визначення в довідковому посібнику не пояснює, чому це проблема. Насправді це прямо називає це неправильним використанням .

len-as-condition (C1801) : не використовувати len(SEQUENCE)як значення умови, що використовується, коли Pylint виявляє неправильне використання len (послідовність) всередині умов.

Мої спроби пошуку також не змогли дати мені більш глибокого пояснення. Я розумію, що властивість довжини послідовності може бути ліниво оцінена, і це __len__може бути запрограмовано на побічні ефекти, але сумнівно, чи це одне досить проблематично, щоб Pylint назвав таке використання неправильним. Отже, перш ніж просто налаштувати свій проект на ігнорування правила, я хотів би дізнатися, чи я щось пропускаю в міркуваннях.

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


9
Тому що ви можете оцінити правдивість послідовності безпосередньо. pylint бажає, щоб ви це зробили if files:абоif not files:
Patrick Haugh

38
lenне знає контексту, в якому він викликається, тому якщо обчислення довжини означає проходження всієї послідовності, вона повинна; він не знає, що результат просто порівнюється з 0. Обчислення булевого значення може зупинитися після того, як він побачить перший елемент, незалежно від того, скільки триває насправді послідовність. Я думаю, що пілінт тут є суперечливим; Я не можу придумати жодної ситуації, коли це неправильно використовувати len, тільки що це гірший варіант, ніж альтернативний.
чепнер

2
@ E_net4 Я думаю, що PEP-8 - це, мабуть, місце для початку.
Патрік Хон


6
SEQUENCES потребує "порожній ()" або "isempty ()", як im + C ++.
JDonner

Відповіді:


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, і часто маю враження, що "Пітонічний шлях" заплутався у власній двозначності.
luqo33

3
Лише загальне питання, чи можна переглянути рекомендації PEP? Ще одна причина, чому на len(s) == 0мою думку є кращою, полягає в тому, що вона є узагальненою для інших типів послідовностей. Наприклад, pandas.Seriesі nummy масиви. if not s:з іншого боку, і в цьому випадку вам потрібно буде використовувати окрему оцінку для всіх можливих типів об’єктів, схожих на масиви (тобто pd.DataFrame.empty).
Марсес

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

Це було проблемою в пілінті, і вона більше не вважається len(x) == 0неправильною.

Не слід використовувати голий len(x) як умову. Порівнюючи len(x)з явним значенням, наприклад, if len(x) == 0з if len(x) > 0абсолютно нормально і не заборонено PEP 8.

Від PEP 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/isissue/1405 & 2684 вирішили цю проблему і було об'єднано, наскільки я розумію, це не повинно бути проблемою при запуску pylint, але Я все ще бачу це питання навіть після оновлення свого пілінгу. Я просто хотів поділитися, як this worked for me, навіть якщо це не зовсім доречно. Але для уточнення, навіть якщо це зайве, якщо ви робите порівняння len (seq) == 0, обрізання не повинно робити нічого, оскільки це вже цілі числа. правильно?
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.