Время прочтения: 6 мин.

Несмотря на то, что мы живём в век стандартизации и очень многие вещи подчиняются таким стандартам, как стандарты ISO (International Organization for standartization) и ГОСТ, единого стандарта для качества кода не появилось до сих пор.

Любое человеческое сообщество стремится к самоорганизации, и сообщество программистов тут не исключение. Стандарты программного кода появляются в компаниях и Open-source сообществах. Также появляются код-конвенции для отдельных языков, которые регулируют правила именования переменных, функций и классов, использования составного оператора и прочих особенностей, характерных для языка. Например, в C# общепринято называть интерфейсы начиная с заглавной буквы I. Это ограничение накладывается не компилятором (отступление от этого правила не приведёт к ошибке компиляции), а сообществом программистов.

3 стандарта для Python

Python не стал исключением среди языков программирования и после многочисленных споров о «табах и пробелах» появились попытки его стандартизировать.

PEP 8

Первым документом, регламентирующим стиль написания кода на Python, был PEP 8. Он напрямую «вытек» из руководства по стилю, составленного создателем Python Гвидо Ван Россумом. Основным стимулом для создания языка, и, впоследствии, для создания этого руководства по стилю, была мысль о том, что код гораздо чаще читается, чем пишется, и поэтому основное внимание надо сконцентрировать на удобочитаемости кода, а также на единообразие кода в широком спектре его применений.

PEP 8 ввёл правило 4 пробелов, по которому каждый уровень отступа должен отделяться четырьмя пробелами от правого края. Для переноса одной строки это правило опционально. Пробелы были признаны основным методом оформления отступов, символ табуляции допускается только тогда, когда остальной код уже был написан с использованием табуляции. Python не позволяет смешивать эти два стиля отступа.

Длина строк была ограничена 79 символами, комментарии или строки документации (docstring) были ограничены 72 символами. Но в документе оставлено замечание о том, что некоторые команды разработчиков предпочитают использовать более длинные строки. В таких случаях допустимо использовать длину строк до 99 символов, оставляя максимальную длину строк комментариев и документации равным 72 символам.

Также PEP 8 регулирует множество других аспектов написания кода на Python, таких как использование «тянущейся запятой» или комментариев. PEP 8 имеет в своём составе конвенцию именования, краеугольным камнем которой является принцип отдачи предпочтения использованию функции или класса, нежели их реализации.

Google Style Guide

Руководство по стилю от Google расширяет собой PEP 8 и описывает такие темы как импортирование пакетов, создание пакетов, обработка исключений и другие аспекты программирования.

Руководство запрещает импорт отдельных классов и функций напрямую, только целых пакетов или модулей. Это сделано для единообразия модулей и простоты управления пространством имён. Источник каждого идентификатора обозначен единообразно x.Obj, что означает, что объект Obj определён в модуле x.

Предписывается использовать полный путь к модулю при импорте. Это позволяет избежать конфликтов в именах модуля или некорректных импортов, появляющихся из-за того, что в пути поиска модулей оказалось то, чего не ожидал автор.

Исключения требуется использовать с осторожностью. Несмотря на то, что исключения позволяют не засорять код другими способами обработки ошибок, они так же запутывают поток управления и упрощают пропуски ошибочных кейсов.

Также регламентируется использование глобальных состояний, вложенных классов и функций, comprehensions и генераторов.

Black

Black считается одним из самых жестких форматов для Python. По заявлению разработчиков, Black является строгим подмножеством PEP 8, то есть даже некоторые разрешённые PEP 8 практики считает неприемлемыми. В отличие от предыдущих руководств, Black исправляет файлы без оглядки на предыдущее форматирование.

Из особенностей Black можно выделить разбиение цепочек вызовов, которые обычно используют ORM-библиотеки. В этом случае точка переносится на новую строку, а аргументы разбрасываются по новым строкам.

Поиск и форматирование кода

В начале рассмотрю использованные для работы модули.

Pylint

Pylint – это один из самый популярных линтеров для Python. Он обеспечивает статический анализ кода и предложения по улучшению и рефакторингу. Стандарт, на соответствие которому проверяется код, хранится в особом файле, согласно конвенция называемом pylintrc. Путь к нему задаётся с помощью параметра —rcfile. В числе других параметров, у pylint есть —ignore, который позволит пропустить определённые пути в папке и не включать их в проверку. Можно управлять форматом вывода через опцию output-format. Существуют ещё опции, которые позволяют просмотреть включенные и отключенные сообщения как для текущей конфигурации, так и для стандартной. И, наконец, с помощью опции –j можно распараллелить процесс на несколько потоков.

Autopep8

Autopep8, как можно догадаться, приводит код к стандарту PEP8. К сожалению, он работает только с файлами, а не с директориями, поэтому нам потребуется реализовать автоматический сбор всех *.py файлов в папке. Они передаются простым списком через пробел. Основным параметром, который управляет процессом приведения кода к стандарту, является –a. Он регулирует, насколько «агрессивно» будет меняться файл. Также можно управлять выведением разности исходных текстов между изначальным и исправленным кодом на экран с помощью —diff, условием, будет ли файл изменён «на месте» используя —in-place и количеством потоков исполнения с помощью –j.

Для удобства код для оценки и исправления был собран в Jupyter-ноутбук. Сначала приведу общий принцип работы этого ноутбука,  а далее представлю пример оценки и исправления кода.

В начале, импортирую нужные библиотеки:

import sys
import os

Далее получаю список всех Python файлов в директории:

all_files = []
for parent_path, _, filenames in os.walk('.'):
    for f in filenames:
        all_files.append(os.path.join(parent_path, f))
all_files = [x for x in all_files if x.endswith('.py') and 'venv' not in x]

Для сравнения, приведу оценки кода до исправления:

!{sys.executable} -m pylint {' '.join(all_files)} --rcfile=pylintrc

Сделанные autopep8 исправления:

!{sys.executable} -m autopep8 {' '.join(all_files)} -i –a

Оценка после исправления:

!{sys.executable} -m pylint {' '.join(all_files)} --rcfile=pylintrc

Результат работы.

После сбора файлов и обработки, средняя оценка файлов в проекте оказалась равна 3,72.

А после исправления кода, оценка увеличилась до 7,01.

Оценка кода увеличилась почти вдвое. Были убраны многие недостатки, типа trailing whitespace.

Таким образом, я получил оценку исходного кода до и после исправления с помощью autopep8, а также сам исправленный код. Применение данного подхода позволяет проверить код перед коммитом. Он также будет полезен для менеджеров проектов, так как позволит не работать с командной строкой, а просто прокликать ячейки ноутбука чтобы проконтролировать выполнение командой разработчиков требований по качеству кода.