Сравнение стандарта PEP8 и «Чистого кода» Роберта Мартина
Предисловие
Привет, Хабр! Признаюсь, честно, за время моего отсутствия я успел по вам соскучиться.
Прежде чем начинать изложение материала, позвольте рассказать небольшую историю, вдохновившую написать эту статью.
Был совершенно обычный день, когда мне в обеденное время написал в ВК знакомый с предложением пройти собеседование на должность разработчика на языке Python. Вакансия очень сильно заинтересовала, поскольку у меня есть большой интерес развиваться в этом языке. Пообщавшись с менеджером, сбросил ему резюме и прикрепил небольшой проект web-серверного приложения.
Главный разработчик провёл ревью и пришёл к выводу, что мне пока рано занимать такую вакантную должность. Вместе с этим HR отправил рекомендации разработчика, как и в каком направлении можно развиваться. Одно из них — чтение книги «Чистый код» под авторством Роберта Мартина
Я сначала не придал особого значения этой книге. За время обучения программированию на Python мне много рекомендовали что почитать. Что-то мне нравилось, что-то нет. Но здесь всё решил один случай.
Через три дня после собеседования я поехал на крупнейшую IT конференцию на Урале DUMP2022. Там познакомился со многими практикующими разработчиками в том числе из этой компании. Какова была моя радость, когда на одной из секций докладчик отметил мой вопрос как лучший, а подарком как раз стала эта книга.
Я понял, что это был знак. Мне действительно надо было прочитать эту книгу. И как оказалось не зря.
Нет, эта статья не очередной обзор, на парадигму автора. Это статья о сравнении двух стандартов PEP8 и «Чистого кода». Вместе с вами я посмотрю чем отличаются эти два стандарта между собой, в чём их сходство. Полученные знания углубят понимание фундаментальных принципов программирования и возможно повлияют на стиль оформления кода.
О «Чистом коде»
Книга Роберта Мартина перевернула мои взгляды о программировании на 180 градусов. До прочтения книги я не задумывался о том, что надо грамотно именовать переменные, грамотно оформлять функции, классы, дизайнерить архитектуру приложения. Про написание юнит тестов вообще промолчу. Всегда считал, что знание юнит тестов больше нужны тестировщику, а не разработчику. Какой же я наивный!
Итогом вышло полное игнорирование архитектуры кода, тестов и так далее. В общем теперь понятно, почему мне отказали в должности.
Когда закончил чтение книги, то понял, что парадигма Чистого Кода является стандартом, к которому стремятся придерживаться крупные IT компании города Екатеринбург (напр. ТОЧКА или СКБ-КОНТУР). И притом совершенно не важно на каком языке я программирую.
Автор пишет код на языке Java. К слову сказать, я не нашёл в интернете какие-либо стандарты (вроде PEP8 в Python), которые могли бы подойти для написания качественного кода на той же Java. Поэтому с осторожностью говорю, что «Чистый код» уже является в какой-то степени набором требований, к которым начинающему разработчику стоит присмотреться. И даже не важно соглашается ли он в полной мере с позицией автора или нет. Тут важно смотреть на альтернативные взгляды о проектировании кода и решать для себя какой подход наиболее подходящий, чтобы читатель кода меня правильно понял.
О стандарте PEP8
Если парадигма Clean Code является стандартом для языка Java, то в Python главным документом, по которому пишется качественный код является PEP8.
За основу написания документа взяты рекомендации создателя языка Python Гвидо ван Россума и дополнения Паула Барри. Программы, написанные по PEP8 читаются в едином стиле и его способен понять каждый Python разработчик. Использование стандарта при разработке программы можно достичь согласованности и единство кода. Я согласен с мнением автора, что согласованность кода со стандартом очень важна. Ведь меня читают другие люди.
Методика сравнения стандартов.
В качестве основного метода сравнения я выделил несколько ключевых слов, встречающиеся в PEP8 и в Чистом коде. Таким образом получился список из 8 пунктов:
Переменные
Функции
Аргументы функции
Комментарии
Обработка ошибок
Юнит тесты
Классы
Модули и системы
Переменные
Переменные находятся на самом низком уровне в программировании. Наверное, каждый согласится, что большая часть программы состоят из переменных. И для того, чтобы люди меня поняли, следует грамотно их описывать. Вот как описывают переменные эти два стандарта
CLEAN CODE
Переменные описываются именем существительным
Для грамотного описания переменной разработчику следует задать самому себе следующие вопросы:
Для чего они используются?
Какую роль они выполняют?
Как они используются в дальнейшем?
Использование стандартной номенклатуры, там где это необходимо. Например, если мы используем декоратор
Использование однозначных имён
PEP8
Переменные отражают то, где они используются, а не что реализуют.
Стандарт предполагает использование переменных в разных стилях
Наиболее предпочтительные для стандарта стили:
1) Одиночная маленькая буква (например «i»)
2) Одиночная заглавная буква (например «A»)
3) Слово в нижнем регистре (например «apple»)
4) Слова из маленьких букв с подчёркиваниями ('one_two_three')
5) Заглавные букв (СapWords, CamelCase) (например: OneTwoThree)
6) Переменные с префиксами (например: _one)
3) Следует избегать назначение в качестве переменных символы l (строчная буква эль), O (заглавная латинская буква «o») и I (латинская буква «ай»).
Выводы по блоку «Переменные»
Исходя из полученной информации на мой взгляд кажется верным факт, что CleanCode дополняет формат PEP8. Однако тут существует различные взгляды на использование префиксов в именовании переменных. Автор Clean Code утверждает, что в качественном коде префиксы не используются. Однако в Python переменные не делятся на публичные и приватные. Чтобы отделить одного от другого для именования переменных используется знак «земля» (_) перед приватным именем.
Таким образом получается закономерный вывод, что если Python разработчик хочет использовать парадигму «Clean Code» то ему следует оставить знак »_» для определения приватных методов, а во всех остальных случаях лишние префиксы исключить. Именование переменных лучше использовать начиная со слова в нижнем регистре или CamelCase стиле
Функции
В программировании функции находятся на втором уровне абстракции. Они описывают поведение переменных в динамике. Динамика определяется преобразованием входящих данных в исходящие. Входящие данные описываются аргументами (их сравнение будет чуть позже), а исходящие данные с помощью встроенного оператора return.
Примером реализации функции на языке Python является следующий код:
def function(arg1, arg2):
newValue = arg1 + arg2
return newValue
В приведённом коде название функции определяется словом, идущим за def после пробельного символа. В скобках описываются аргументы, которые она принимает. И результатом выполнения данной функции является некоторое новое значение. Стоит также отметить что представленный код считается плохим с точки зрения дизайна, но хорошим в плане объяснения и понимания функции. Далее сравниваем стандарты.
CLEAN CODE
Функция описывается глагольной формой и служит для описания поведения переменных.
Автор рекомендует называть функцию так, чтобы она выполняла только одно действие. Эта рекомендация сопоставима с принципами SOLID.
Размер функции ограничивается 20 строками в ширину и 120 символами в длину
Не рекомендуется использовать дублирующий код.
Не рекомендуется возвращать пустые значения в операторе return.
PEP8
Рекомендуется использовать такой же стиль написания, как и при именовании переменных. Например, если мы называем переменные в стиле CamelCace, следовательно и функции мы называем в CamelCase.
Для отделения одной функции от другой рекомендуется использовать 4 пустых строки
Длина строки ограничивается максимум 79 знаками
Выводы по блоку «Функции».
По оформлению функций тоже видно, что сравниваемые стандарты сопоставимы и в какой-то степени дополняют друг друга. Различие описывается правилами оформления. То есть, если Мартин определяет длину кода 120 символами, то по PEP8 она ограничивается 79. Но тут мне понравилась рекомендация Мартина. Он говорит, что правильно написанная функция по оформлению сопоставима с форматом монитора на ПК. В идеале, конечно, чтобы написанный код не выходил за рамки монитора.
Аргументы функции.
Аргументы функции я немного затронул в предыдущем разделе. Здесь я немного расширю их понимание. По сути своей аргументы — это часть функции. Они являются исходными данными для их преобразования в другие данные. Ниже рассмотрим как аргументы понимает Clean Code и PEP8
CLEAN CODE
Чем меньше аргументов принимает функция, тем легче она читается.
Считается, что, если функция принимает три и более аргументов — это плохая функция.
. Не допускается использовать Null (None) в качестве аргумента функции.
PEP8
Вызов аргумента не должен отделяться пробельным символом между именем функции и значением аргумента.
Не допускается написание пробелов, если явно задано значение аргумента.
Не допускается использование зарезервированных слов в качестве аргументов функции.
Выводы по блоку «Аргументы функции».
В целом разделы из CLEAN CODE и PEP8 также являются сопоставимыми. Примерно одинаковую по объёму информацию можно найти как в CLEAN CODE, так и в PEP8.
Комментарии
Комментарии являются главным инструментом, описывающим программное обеспечение ВНЕ кода. Ещё в университете меня учили тому, что к любому коду нужно писать комментарий. Каково было моё удивление осознать, что в реальных проектах чем меньше комментариев, тем лучше.
В языке Python комментариями является текст, который начинается либо с решётки (#), либо с тройных кавычек. Примеры комментариев представлены ниже.
#Комментарий
def function(arg1, arg2):
"""
Это тоже комментарий
:param arg1:
:param arg2:
:return:
"""
newNumber = arg1 + arg2
return newNumber
.Далее рассмотрим как видят комментарии авторы CLEAN CODE и PEP8.
CLEAN CODE
Любой комментарий в коде является предвестником того, что он написан плохо.
Они очень часто содержат неправдивую информацию, их достаточно сложно поддерживать и обновлять.
Они не отражают точное поведение функции
Если у программиста появляется мотивация писать комментарий, то это является первым признаком, что он пишет плохой код. Исключениями могут быть комментарии, содержащие авторское право и комментарии, начинающиеся с флагом #TO DO:
PEP8
Комментарии, противоречащие коду, хуже, чем отсутствие комментариев.
Комментарии должны являться законченными предложениями.
Старайтесь реже использовать подробные комментарии.
Выводы по блоку «Комментарии».
В Clean Code уделяется комментариям целая глава. Исходя из этого автор разбирает их более подробно, чем в PEP 8. Если Вы новичок в программировании, то вероятнее всего знать правила оформления комментариев по PEP8 будет достаточно. Но если выходить на реальные проекты, то вместе с PEP8 следует учитывать правила комментирования по Clean Code.
Обработка ошибок
Обработка ошибок описывается стандартным набором правил, которые предусматривают, что программист заранее знает, что программный код вернёт ошибку. Примером, описывающим применение этого инструмента служит блок try.
Далее представлю пример использования этого блока на языке Python. За основу я взял задачу из тренажёра CODE WARS Найти задачу можно тут. Кстати, вот пример того, как я её реализовал.
def find_function(func, arr):
res = []
for value in func:
try:
if value.__name__ == '':
for arr_value in arr:
if value(arr_value):
res.append(arr_value)
except AttributeError:
continue
return res
Здесь применяется исключение для того, чтобы цикл смог без ошибки AttributeError завершить свою работу. Этот код был написан до прочтения Clean Code. Так что не судите слишком строго. Теперь рассмотрим что предлагают авторы CleanCode и PEP8.
CLEAN CODE
Очень желательно, чтобы ошибки не пришлось обрабатывать в коде.
Любое исключение нарушает логику работы интерпретатора.
Если не понять причины возникновение ошибок, то это в будущем приведёт к неработающему коду.
Обработка исключений является нарушением принципов SOLID
Если программист понимает, что вызова try не избежать то автор рекомендует придерживаться следующих правил:
Перед вызовом функции необходимо её проверить на ошибки.
Все ошибки обрабатывать с помощью блока «try». Условные операторы типа «if-else» при обработке исключений лучше не использовать.
Применение «try» лучше всего подходит для тех случаев, когда нужно исключить ненужный код.
Использовать try необходимо в том коде, который обладает наибольшей уязвимостью.
PEP8
Стандарт налагает ограничение на использование блоков TRY в программировании. Использование исключений допускается только в двух случаях:
Если обработчик выводит пользователю всё о случившейся ошибке; по крайней мере, пользователь будет знать, что произошла ошибка.
Если нужно выполнить некоторый код после перехвата исключения, а потом вновь «бросить» его для обработки где-то в другом месте.
Авторы стандарта рекомендуют заключать в каждую конструкцию try…except минимум кода. Это необходим для того, чтобы легче отлавливать ошибки.
Выводы по блоку «Обработка ошибок».
Оба автора рекомендуют как можно меньше использовать инструмент по обработке ошибок и исключений в коде. Так же как и в предыдущем разделе в Clean Code автор рассматривает больше случаев, в котором допускается обработка ошибок. Поэтому вывод по данному разделу такой же как и по предыдущему. Если использовать Python для обучения языку, то стандарты PEP8 must have. Если вы претендуете на должность Junior разработчика, то знаний PEP8 будет уже недостаточно для прохождения собеседования. Желательно хотя бы знать базовые правила Clean Code.
ЮНИТ ТЕСТЫ
Я был слегка удивлён, когда при трудоустройстве на позицию Junior разработчика компания требует знать хотя бы основы работы Юнит Тестов. Всегда думал, что эта работа больше требуется тестировщикам, а не разработчиком. В данном разделе также следует знать, что разработка юнит-тестов не описывается стандартом PEP8.
По крайней мере мне такой информации найти не удалось. А вот, что пишет о них автор CLEAN CODE.
CLEAN CODE
Наличие юнит-тестов по степени важности находятся наравне с функциями, описывающими логику программного кода.
Тесты необходимы для того, чтобы разработчик программного обеспечения смог убедиться в том, что его код работает так, как он ожидает.
Написание грамотных тестов описываются правилами TDD (Test Driven Development) и состоит из трёх пунктов:
Пока нет юнит тестов, код нельзя допускать до массового потребителя
Тестов всегда мало. Поэтому чем больше юнит тестов, тем больше уверенности в корректности работы программы.
Если хотя бы один тест не удовлетворяет требованиям программы, то такую программу нельзя допускать до массового потребителя.
Юнит тесты должны быстро выполняться.
Предполагается, что они очень часто запускаются.
Они работают независимо друг от друга.
Они успешно запускаются на любой платформе и операционной системе.
Тесты выполняются без вмешательства их разработчика.
Выводы по блоку «Юнит Тесты».
В стандарте PEP8 не описываются правила по разработке юнит тестов. Для успешного прохождения собеседования к уже знакомому PEP8 надо посмотреть как пишутся юнит-тесты на Python. Если в PEP8 не уделяется внимание юнит тестам, то за концептуальную основу можно взять CLEAN CODE.
Классы
Классы являются основой объектно-ориентированного программирования. Благодаря им мы можем разрабатывать сложные системы. В основу класса входят методы (они же описываются функцией). Примером класса, может являться например Корабль. Он обладает определённым набором функций, которые позволяют им управлять.
Далее я приведу пример класса на языке Python. Данный класс является решением Этой задачи. Это не идеальное решение. Уверен у вас будет лучше)
class Ship:
def __init__(self, draft, crew):
self.draft = draft
self.crew = crew
def is_worth_it(self):
draft_crew = self.crew * 1.5
worth = self.draft - draft_crew
return False if worth < 20 else True
Опишу как видят классы авторы CLEAN CODE и PEP8.
CLEAN CODE
Классы именуются именами существительными.
Чем длиннее название класса, тем меньше количество функций он в себе содержит.
Класс должен соответствовать принципам SOLID.
В классе содержится подробное описание его зоны ответственности.
Класс должен содержать малое количество переменных и функций.
Устройство класса:
Сначала объявляются публичные атрибуты класса.
Потом идут приватные атрибуты класса.
Затем идут публичные функции.
Далее приватные.
PEP8
Имена классов должны обычно следовать соглашению CapWords.
PEP8 предполагает использование self в качестве первого аргумента метода экземпляра объекта.
Чтобы избежать конфликтов имен с подклассами, используйте два ведущих подчеркивания (читайте префиксы).
Обязательно решите, каким должен быть метод класса или экземпляра класса (далее — атрибут) — публичный или непубличный.
Выводы по разделу «Классы»
В Python организация классов разрабатывается несколько иначе, чем на Java. Поэтому к применению Clean Code в Python разработке следует подходить с особой осторожностью. Например, если в языке Java надо явно определять тип класса (public, private, protected), то Python такой возможности не имеет. Поэтому применение префиксов внутри класса становится оправданным, хотя в Clean Code их использование явно не приветстывуется.
Системы
Система — представляет собой набор из множества классов. Любой класс является частью системы. Примером большой системы может являться работа города. Управлять целым городом не под силу одному человеку. Поэтому у любого мэра есть определённый штат сотрудников, у которых есть свои обязанности. Если говорить, например, про Екатеринбург, то у главы города в подчинении находятся руководители администрации районов. Руководители районов с одной стороны находятся в подчинении главы Екатеринбурга, с другой стороны имеют свободу независимо от него решать поставленные сверху задачи.
В стандарте PEP8 работа системы описывается стандартами работы модуля
В программной реализации системой могут являться готовые модули. Их программную реализацию будет достаточно трудно описать, поэтому здесь будет приведено только сравнение взглядов авторов.
CLEAN CODE
Необходимо разделять процесс проектирования системы от её эксплуатации.
К этим процессам надо подходить с разных позиций. Можно рассмотреть процесс проектирования на примере строительства отеля. Когда его строят, то в процесс задействуются совершенно разные механизмы. Эти механизмы описываются единым общим процессом «строительство». Когда люди начинают его эксплуатировать, то здесь уже нанимается персонал, который отвечает не за строительство, а за обслуживание. Иными словами, процесс строительства и обслуживания никак не связаны между собой, за исключением только разве что наличия общего объекта.
Программные продукты, которые не учитывают разделение имеют шанс иметь проблемы с выполнением тестов.
Без разделения программных систем нарушается принцип единственной ответственности.
Следует отделять идейный код от работающего кода.
С ростом кода становится сложнее поддерживать системы.
PEP8
Модули должны иметь короткие имена, состоящие из маленьких букв.
Можно использовать символы подчеркивания, если это улучшает читабельность.
Новые модули и пакеты должны быть написаны согласно стандарту PEP8, но если в какой-либо уже существующей библиотеке эти правила нарушаются, предпочтительнее писать в едином с ней стиле c с программой, нарушающей стандарты.
Выводы по блоку «Системы».
Разработку систем авторы стандартов видят с разных точек зрения. Если авторы Clean Code видят систему как совокупность объектов с устоявшимися связями, то по PEP8 аналогом такой системы является модуль. В данном разделе я тоже склоняюсь к мнению, что использовать CleanCode при проектировании системы надо с осторожностью. Главное не нарушать принципы PEP8.
Общие выводы
Данный сравнительный анализ описывает далеко не все аспекты программирования. Он весьма поверхностный. Однако полученные выводы помогут дать ответ на вопрос: что можно использовать из книги Clean Code для разработки на Python и что использовать с осторожностью. Данная статья представляет взгляд новичка на проблему проектирования. Я понимаю, что для многих даже профессионалов эта тема даётся нелегко. Поэтому буду рад замечаниям и дополнениям. Спасибо за внимание!