Холиварный рассказ про линтеры |
||
|
МЕНЮ Главная страница Поиск Регистрация на сайте Помощь проекту Архив новостей ТЕМЫ Новости ИИ Голосовой помощник Разработка ИИГородские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Искусственный интеллект Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Нейронные сети начинающим Психология ИИ Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Творчество ИИ Техническое зрение Чат-боты Авторизация |
2021-08-04 11:21 Все мы пишем код. Много кода. Само собой, бывают ошибки. Иногда это просто кривой код, а иногда цена ошибки — взорванный космический корабль. Конечно, никто не делает намеренных косяков, все в меру возможностей стараются следить за качеством, но без инструментов статического анализа вряд ли можно быть уверенным, что всё идеально. Линтеры помогают приводить код к единому стилю и избегать ошибок. Правда, только в том случае, если вы готовы к страданиям, а не отмахиваетесь в конце концов «pylint: disable», только чтобы оно отстало. Какой должен быть линтер, и почему таки не обойтись Pylint, знает Никита Соболев (sobolevn), который понимает и любит линтеры настолько, что даже свою компанию назвал так, чтобы их не расстраивать — wemake.services.
![]() Зачем нам линтеры?
Самая важная задача линтеров — приводить код к единообразию. Есть много вариантов написать одно и то же на Python: поставить запятую здесь или там, забыть закрыть скобки или не забыть. Когда люди долго пишут код, он становится похож на лоскутное одеяло из сшитых в разное время разрозненных кусков. Работать с таким одеялом неприятно, он отбивает желание читать код, а это очень плохо. Какие бывают линтеры? Самые простые проверяют только стиль, например, Flake8. В какой-то степени ещё и Black, но скорее это автоформатор-линтер. Линтеры сложнее проверяют семантику, а не только стилистику: что вы делаете, зачем, и бьют вас по рукам, если пишете с ошибками. Хороший пример — Pylint, который мы все знаем, пользуемся и любим. Я называю такие линтеры — Best practices. Третий тип — Type checking, эти линтеры немного в стороне. Type checking в Python — новинка, её сейчас делают две конкурирующие платформы: Mypy и Pyre. Как применять линтеры? Я не утверждаю, что линтеры — это панацея и замена всему. Это не так. Линтеры — первая ступенька пирамиды, по которой код попадает в продакшен.
![]() Используете ли вы на работе линтер? Если спросить разработчиков из сурового enterprise, в котором трудятся по 7 дней в неделю, применяют ли они линтер, то выяснится, что хотя бы треть из них используют линтеры очень строго: CI падает, проверки суровы. Остальные примерно в равной степени применяют линтеры только для проверки стиля, никогда и как отчётную систему: запускают линтер, генерируют отчет и смотрят, насколько всё плохо. Линтеры используются, и это хорошо. В нашей компании всё построено очень сурово: жёсткий линтинг, очень много проверок, двойной код-ревью. Код-ревью
Проблемы возникают как раз на этом этапе. Это верхняя и самая сложная ступень пирамиды: код-ревью автоматизировать не получится, а если и возможно, то это приведёт к автоматизации написания кода. Тогда и программисты станут не нужны. Pylint
В эфире рубрика «Почему не Pylint?» Этот вопрос я слышал много раз. Отвечу на него помягче. Pylint — прекрасный инструмент, рок-звезда для кода на Python, но у него есть особенности, которые я не хочу видеть в своём линтере. SonarQube
Прекрасный, но отдельный инструмент, который живёт где-то рядом с вашим проектом.
Компания, которая занимается развитием SonarQube, специфически смотрит на концепцию развития продукта. Это может быть проблемой. Достоинство SonarQube в том, что у него очень крутые проверки, которые показывают сложность, возможные скрытые ошибки и баги. Проверки мне нравятся, я бы их оставил, а платформу — поменял. Flake8
Замечательный линтер — очень простой, но с одной проблемой: мало правил, с помощью которых он проверяет, насколько код хорошо написан. При этом у Flake8 есть очень много очень простых плагинов: минимальный плагин — это 2 метода, которые нужно реализовать. Я подумал — давайте возьмём Flake8 как основу и напишем плагины, но со своим пониманием пользы для компании. И мы так и сделали. Самый строгий линтер в мире
Мы сделали инструмент, в котором собрали всё, что считаем правильным для Python и назвали wemake-python-styleguide. Плагин выложили публично, так как я считаю, что Open Source by Default — это хорошая практика. Я глубоко убежден, что многие инструменты выиграют, если их выложат в Open Source. Для нашего инструмента мы придумали слоган: «Самый строгий линтер в мире!» Ключевое слово в нашем линтере — строгий, что значит боль и страдания. Если вы пользуетесь линтером, и он не заставляет вас страдать так, что хватаешься за голову: «Да что же тебе ещё не нравится, будь ты проклят», то это плохой линтер. Он пропускает ошибки, недостаточно следит за качеством кода, и нам не нужен. Нам нужен самый строгий в мире, который многое проверяет. Сейчас у нас порядка 250 разных проверок в обоих категориях: стилистических и Best practices, но без Type checking. Им занимается Mypy, мы к нему никак не относимся. У нашего линтера нет компромиссов. У нас нет правил из разряда «Не хотелось бы это делать, но если сильно хочется, то можно». Нет, мы всегда говорим жёстко — это не делаем, потому что плохо. Потом приходят люди и говорят: «Есть же 2,5 use case, где это в принципе возможно!». Если такие кейсы, явно напиши, что здесь эта строчка позволительна, чтобы линтер её игнорировал, но объясни почему. Это должен быть комментарий, почему ты разрешил какую-то странную практику и зачем это делаешь. Этот подход еще и полезен для документирования кода. Самый строгий линтер не требует настроек (WIP). У нас пока есть настройки, но мы хотим от них избавиться: имея свободу, пользователь обязательно настроит так, что линтер будет работать неправильно. Хороший инструмент в настройках не нуждается — в нем хорошие значения по умолчанию. С таким подходом код будет консистентный и будет работать у всех одинаково, по крайней мере, в теории. Мы над этим ещё работаем, и пока настройки есть, можете пользоваться нашим инструментом и настраивать его под себя. От кого зависим? От большого количества инструментов.
Что проверяем? Есть 4 группы правил, которые мы используем и заставляем соблюдать. Что такое сложность?
У сложности есть конкретные метрики, на которые можно посмотреть и сказать — сложно или нет. Их много. Водопад сложности
Сложность начинается с того, что мы написали строчку, и она пока хорошая. Но потом приходит бизнес и говорит, что цены поднялись в два раза, и мы умножаем на 2. В этот момент Jones Complexity сходит с ума и сообщает, что теперь строчка слишком сложная — там слишком много логики. Холивар
Давайте похоливарим, комментарии открыты. Сначала, напомню, что имена — сложная и нерешенная проблема. Можно подраться из-за того, как назвать переменную, но у нас есть некоторые подходы, которые помогают хотя бы не сделать явных ошибок. Имена
Как вам такие: var, value, item, obj, data, result? Что такое data? Какие-то данные. Что такое result? Какой-то результат. Часто вижу переменную result и вызов какого-то адского метода у непонятного класса — и думаю: «Что это за result? Зачем он здесь?» Называть переменные одной буквой — нормально?
Например, q? Все мы знаем классический кейс: Половина разработчиков считает, что называть переменные i, х, у, z — это нормально. Я считаю, что называть имена одной буквой нельзя. Хочу больше контекста и хорошо, что со мной согласна вторая половина разработчиков. Если в C это еще как-то допустимо из-за исторического наследия, то в Python это очень большая проблема и так делать не надо. Консистентность
Давайте просто выберем один способ из многих, и скажем: «Давайте делать так». Хорош он или плох — уже не важно — просто консистентно. F-строки ужасны?
Варианты ответов:
Есть гипотеза, что f-строки ужасны. В них засовывают что угодно! f-строки — это не то же самое, что .format, отличия кардинальны. Когда мы объявляем некий шаблон, а потом его форматируем, то совершаем два действия по отдельности: сначала определяем шаблон, а потом форматируем. Когда мы объявляем f-строку, о совершаем одновременно два действия: сразу объявляем шаблон и форматируем — в один и тот же момент.C f-строками бывает две проблемы. Мы объявили шаблон для f-строки и всё работает. А потом мы решаем перенести шаблон на 2 строки вверх или вынести в другую функцию — и всё ломается. Теперь нет контекста, который позволял форматировать строки, и мы не можем их корректно обрабатывать. Вторая большая проблема с f-строками: они позволяют делать страшное — засовывать логику в шаблон. Допустим, есть строка, в которой просто вставляем имя пользователя и слово «Привет» — это нормально. Особо страшного ничего нет, но потом мы видим, что имя пользователя приходит заглавными буквами, решаем перевести его в Title case и пишем прямо в шаблоне username.title(). Потом в шаблоне появляются условия, циклы, импорты. И все остальные части php.Все эти проблемы заставляют меня сказать, что f-строки — плохая тема, мы их не используем. Самое смешное, что у нас нет кейса, в котором нам подходят только f-строки. Обычно подходит любое форматирование, но мы выбрали .format — все остальное нельзя — ни %, ни f-строки. Работу .format тоже линтим, потому что у него внутри можно ставить фигурные кавычки и писать либо имя переменной, либо ее порядок.Во время доклада количество противников f-строк выросло с 33 до 38% — это маленькая, но победа. Числа
Любите ли вы такие числа: В итоге мы осознали, что за этим нужно следить, и никакие магические числа в код не пропускаем. Хорошо, что с нами согласны почти 100% коллег, и тоже не используют такие числа. Но есть исключения — это числа от ?10 до 10, числа 100, 1000 и подобные, просто потому, что они часто встречаются и без них сложно. Мы жесткие, но не садисты и немного думаем. Используете ли вы ’@staticmethod’?
Давайте подумаем, что такое staticmethod. Задумывались ли вы, зачем он в Python? Я — нет. У меня был прекрасный Pylint, который говорил: Логика в __init__.ру — хорошо или плохо?
Это моя любимая тема. Наверняка, когда вы создаете новый пакет и как-то его называете — у него создаётся __init__.ру и вы задумываетесь, что в него положить? Что поместить в __init__.ру, а что — в файлики рядышком? Для меня это был нетривиальный вопрос, и я всегда терялся: наверное, что-то самое важное? Потом я подумал — нет, наоборот, самое важное помещу в самый понятный контекст. Если положить что-то в __init__.ру, и потом это все импортить, получаются циклические импорты — тоже плохо. Функция hasattr часто вам нужна?
Как часто нужна функция hasattr? Мне кажется, что достаточно часто, потому что в Python динамическая типизация — утиная. Нам hasattr иногда нужен, чтобы проверить наличие аргумента или атрибута у класса (провокация). Что мы хотим добавить в наш линтер
Этого нет в нашем линтере, но мы очень хотим layer-linter. Что он делает? Вы задаете в текстовом формате контракты: что можно импортировать, а что нет, в каких местах можно импортировать, а в каких нет. Вы создаете контракт на то, как внутри вашего кода бизнес-логика будет поделена на слои. Благодаря этому вы получаете отличный прирост качества без каких-либо телодвижений. Очень рекомендую. Я уже говорил про cohesion. У нас нет этого плагина в основе, но мы его используем. Cohesion смотрит, насколько связан ваш класс внутри. У него есть достаточно много False Positive ошибок и использовать его в продакшене нельзя, но мы его применяем для аналитики — смотрим, какие классы хорошие, какие плохие. Плагин vulture использует поиск неиспользуемого кода в Python и позволяет его удалять. Из-за того, что Python очень динамический язык, плагин дает много погрешностей. Поэтому мы его применяем как сohesion. Radon позволяет смотреть разные метрики вашего кода, их очень много: Halstead, Maintainability Index, цикломатическая сложность. Попробуйте, прогоните код и посмотрите его коэффициент — это круто. Final type
Я люблю Final-классы в Python. Их недавно добавили в Typing Extensions, а до этого у меня был собственный пакет, который я написал сам. Я считаю, что если ты сделал какой-то класс, и его больше нельзя наследовать — это хорошо, потому что ты просто зафиксировал реализацию. Если человек говорит, что он все-таки хочет что-то изменить, то зачем? Не надо. Используй композицию. Если что-то менять — напиши документацию, и тогда, возможно, можно. Gratis
Наш линтер появился благодаря труду многих людей, и я им очень благодарен. ![]() Кстати, Никита Соболев вступил в программный комитет Moscow Python Conf++, и помогает в подготовке классной программы. Конференция через два месяца, а у нас уже отобрано две трети докладов, можно изучить их тут и решить участвовать в нашем продуктивном мероприятии для Python-программистов. А теперь разожжем немного холивара посредством интерактива. Результаты голосования во время выступления под спойлером. Проголосуйте и проверьте, а так ли поступает большинство разработчиков, или читерски подсмотрите, а потом топите за свой вариант. Источник: habr.com Комментарии: |
|