PEP 8: комментарии, пробелы, выбор методов

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

Комментарии

 «Если реализацию сложно объяснить – идея точно плоха», — Дзен Python.

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

Вот некоторые ключевые моменты, которые следует помнить при добавлении комментариев:

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

Комментарии к блокам кода

Используйте блочные (т.е. многострочные) комментарии, чтобы задокументировать небольшой фрагмент кода. Такие комментарии полезны, когда вам нужно написать несколько строк кода для выполнения одного действия, например, для импорта данных из файла или обновления записи в базе данных. Они важны, поскольку помогают другим понять цель и функциональность данного блока кода.

PEP 8 предлагает следующие правила для написания комментариев к блокам кода:

  • Отступайте в комментариях блока до уровня кода, который они описывают.
  • Каждую строку начинайте с символа #, за которым следует один пробел.
  • Разделяйте абзацы строкой, содержащей один символ #.

Вот комментарий блока, объясняющий назначение цикла for. Обратите внимание, что предложение переносится на новую строку, чтобы сохранить ограничение в 79 символов:

for i in range(0, 10):
    # Проходим цикл переменной i 10 раз и выводим на экран её значение,
    # а затем знак переноса строки
    print(i, '\n')

Иногда, если код очень технический, необходимо использовать более одного абзаца в комментарии блока:

def quadratic(a, b, c, x):
    # Посчитать решение квадратного уравнения при помощи
    # формулы дискриминанта.
    #
    # У квадратного уравнения всегда есть 2 корня: x_1 и x_2.
    x_1 = (- b+(b**2-4*a*c)**(1/2)) / (2*a)
    x_2 = (- b-(b**2-4*a*c)**(1/2)) / (2*a)
    return x_1, x_2

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

[python_ad_block]

Встроенные комментарии

Встроенные (inline) комментарии объясняют один оператор в фрагменте кода. Они полезны, чтобы напомнить вам или объяснить другим, почему необходима определенная строка. Вот что о них говорит PEP 8:

  • Используйте встроенные комментарии экономно.
  • Пишите встроенные комментарии в той же строке, что и утверждение, к которому они относятся.
  • Отделяйте встроенные комментарии от кода двумя или более пробелами.
  • Начинайте встроенные комментарии с символа # и одного пробела, как и комментарии для блоков.
  • Не используйте их для объяснения очевидного.

Ниже приведен пример встроенного комментария:

x = 5  # Это встроенный комментарий

Порой кажется, что без встроенного комментария не обойтись, но иногда проблему решает просто удачное название переменной. Вот пример:

x = 'John Smith'  # Имя студента

Здесь встроенный комментарий дает дополнительную информацию. Однако использование x в качестве имени переменной для имени человека — плохая практика. Если вы переименуете переменную, то отпадёт и необходимость во встроенном комментарии:

student_name = 'John Smith'

Наконец, встроенные комментарии, подобные этим, являются плохой практикой, поскольку они поясняют очевидный и беспорядочный код:

empty_list = []  # Инициализировать пустой список

x = 5
x = x * 5  # Умножить x на 5

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

Строки документации

Строки документации (docstrings) — это строки, заключенные в три пары двойных (""") или одинарных (''') кавычек. Они пишутся в первой строке любой функции, класса, метода или модуля. Вы можете использовать их для объяснения и документирования конкретного блока кода. Существует отдельный PEP — PEP 257, который охватывает эту тему, но вы получите краткое изложение в этом разделе.

Наиболее важные требования к строкам документации следующие:

  • Окружайте строки документации тремя двойными кавычками с каждой стороны. Вот так: """Это строка документации""".
  • Пишите их для всех общедоступных (public) модулей, функций, классов и методов.
  • Помещайте кавычки, завершающие ваш многострочный комментарий, на отдельной строке:
def quadratic(a, b, c, x):
    """Решим квадратное уравнение через дескриминант.

    Форма квадратного уравнения:
    ax**2 + bx + c = 0

    У квадратного уравнения всегда 2 решения: x_1 & x_2.
    """
    x_1 = (- b+(b**2-4*a*c)**(1/2)) / (2*a)
    x_2 = (- b-(b**2-4*a*c)**(1/2)) / (2*a)

    return x_1, x_2
  • Для однострочных комментариев оставляйте закрывающие кавычки """ в той же строке:
def quadratic(a, b, c, x):
    """Решаем через дискриминант"""
    x_1 = (- b+(b**2-4*a*c)**(1/2)) / (2*a)
    x_2 = (- b-(b**2-4*a*c)**(1/2)) / (2*a)

    return x_1, x_2

Более подробную информацию о документировании кода Python читайте в статье «Documenting Python Code: A Complete Guide» Джеймса Мертца.

От редакции Pythonist. Рекомендуем статью «Составление документации для проектов на Python».

Пробелы в выражениях и утверждениях

 «Разреженное лучше плотного», — Дзен Python.

Пробелы могут быть очень полезны в выражениях и операторах при правильном использовании. Если пробелов недостаточно, могут возникнуть трудности при чтении кода: он сливается в сплошное полотно. Если пробелов слишком много, может быть сложно визуально объединить связанные между собой операторы.

Пробелы вокруг двоичных операторов

Следующие бинарные операторы нужно окружать пробелами с обеих сторон:

  • Операторы присваивания (=, + =, — = и т. д.);
  • Сравнения (==,! =,>, <.> =, <=) и (is, is not, in, not in);
  • Логические операторы (and, not, or).

Замечание. Когда = используется для присвоения значения по умолчанию аргументу функции, не окружайте его пробелами.

# Рекомендуется
def function(default_parameter=5):
    # ...


# Не рекомендуется
def function(default_parameter = 5):
    # ...

Когда в инструкции содержится более одного оператора, добавление одного пробела до и после каждого оператора может привести к путанице. Вместо этого лучше добавлять пробелы только вокруг операторов с самым низким приоритетом, особенно при выполнении математических манипуляций. Вот пара примеров:

# Рекомендуется
y = x**2 + 5
z = (x+y) * (x-y)

# Не рекомендуется
y = x ** 2 + 5
z = (x + y) * (x - y)

Вы также можете применить это к операторам if с несколькими условиями:

# Не рекомендуется
if x > 5 and x % 2 == 0:
    print('x больше 5 и чётное!')

В приведенном выше примере оператор and имеет самый низкий приоритет. Поэтому будет более ясным следующее выражение if:

# Рекомендуется
if x>5 and x%2==0:
    print('x больше 5 и чётное!')

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

Следующее недопустимо:

# Никогда так не делайте!
if x >5 and x% 2== 0:
    print('x больше 5 и чётное!')

В срезах двоеточия действуют как бинарные операторы. Следовательно, применяются правила, изложенные в предыдущем разделе, и с каждой стороны должно быть одинаковое количество пробелов. Допустимы следующие варианты оформления срезов списка:

list[3:4]

# Двоеточие - оператор с наименьшим приоритетом
list[x+1 : x+2]

# В расширенном срезе оба двоеточия следует
# обособить одинаковым количеством пробелов
list[3:4:5]
list[x+1 : x+2 : x+3]

# Если не указан параметр среза, опускается и пробел
list[x+1 : x+2 :]

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

Когда избегать добавления пробелов

В некоторых случаях добавление пробелов может затруднить чтение кода. Слишком много пробелов может сделать код слишком разреженным и трудным для понимания. PEP 8 дает четкое описание примеров, когда пробелы неуместны.

Самое важное место, где следует избегать пробелов, – это конец строки. Это называется завершающим пробелом (trailing whitespace). Такой пробел невидим и может приводить к ошибкам, которые трудно отследить.

В следующем списке перечислены некоторые случаи, когда вам не следует добавлять пробелы:

  • Сразу после открывающей скобки и непосредственно перед закрывающей:
# Рекомендуется
my_list = [1, 2, 3]

# Не рекомендуется
my_list = [ 1, 2, 3, ]
  • Для отделения запятой, точки с запятой или двоеточия:
x = 5
y = 6

# Рекомендуется
print(x, y)

# Не рекомендуется
print(x , y)
  • Перед открывающей скобкой, с которой начинается список аргументов вызова функции:
def double(x):
    return x * 2

# Рекомендуется
double(3)

# Не рекомендуется
double (3)
  • Перед открывающей скобкой, с которой начинается индекс или срез:
# Рекомендуется
list[3]

# Не рекомендуется
list [3]
  • Между запятой и закрывающей круглой скобкой:
# Рекомендуется
tuple = (1,)

# Не рекомендуется
tuple = (1, )
  • Чтобы выровнять операторы присваивания:
# Рекомендуется
var1 = 5
var2 = 6
some_long_var = 7

# Не рекомендуется
var1          = 5
var2          = 6
some_long_var = 7

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

Рекомендации по программированию

 «Простое лучше сложного», — Дзен Python.

Мы часто обнаруживаем, что в Python (как и в любом другом языке программирования) бывает несколько способов выполнить одно и то же действие. В этом разделе мы рассмотрим несколько предложений из PEP 8, призванных устранить эту двусмысленность и сохранить согласованность.

Не сравнивайте логические значения с True или False при помощи оператора эквивалентности (==)

Вам часто придется проверять, является ли логическое значение истинным или ложным. Кажется очевидным, что это стоит сделать при помощи оператора ==:

# Не рекомендуется
my_bool = 6 > 5
if my_bool == True:
    return '6 больше чем 5'

Но на самом деле здесь нет необходимости в использовании оператора эквивалентности. bool может принимать только значения True или False. Поэтому достаточно написать следующее:

# Рекомендуется
if my_bool:
    return '6 больше чем 5'

Этот способ выполнения оператора if с логическим значением требует меньше кода и выглядит проще, поэтому PEP 8 поощряет его.

Используйте в if-блоках тот факт, что пустые последовательности ложны

Если вы хотите проверить, пуст ли список, у вас может возникнуть соблазн проверить длину списка. Если список пуст, его длина равна 0, что эквивалентно False при использовании в операторе if. Вот пример:

# Не рекомендуется
my_list = []
if not len(my_list):
    print('Список пуст!')

Однако в Python любой пустой список, строка или кортеж является False. Таким образом, мы можем предложить более простую альтернативу приведенному выше коду:

# Рекомендуется
my_list = []
if not my_list:
    print('Список пуст!')

Хотя в обоих примерах будет выведено, что список пуст, второй вариант проще, поэтому PEP 8 поддерживает его.

Используйте is not, а не not … is в операторах if

Если вы пытаетесь проверить, имеет ли переменная какое-нибудь значение, есть два варианта. Первый – использовать оператор if с x is not None, как в примере ниже:

# Рекомендуется
if x is not None:
    return 'x существует!'

Второй вариант — оценить x is None, а затем применить оператор if, исходя из результата оператора not:

# Не рекомендуется
if not x is None:
    return 'x exists!'

Хотя оба варианта выполнятся правильно, первый проще, поэтому PEP 8 рекомендует его.

Не используйте if x: если вы имеете в виду if x not None:

Иногда у вас может быть функция с аргументами, которые по умолчанию равны None. При проверке, присвоено ли такому аргументу arg другое значение, часто допускают следующую ошибку:

# Не рекомендуется
if arg:
    # Что-то происходит с arg...

Этот код проверяет истинность аргумента. Но на самом деле вы хотите проверить, что arg не равен None, поэтому было бы лучше использовать следующее:

# Рекомендуется
if arg is not None:
    # Что-то происходит с arg...

Здесь ошибка состоит в том, что not None и истинность приравниваются. Но вы могли установить arg = []. Как мы видели выше, пустые списки в Python считаются ложными. Таким образом, даже несмотря на то, что аргумент arg был назначен, условие не выполняется, и поэтому код в теле оператора if игнорируется.

Используйте .startswith() и .endswith() вместо срезов

Если вы пытались проверить, было ли слово префиксом или суффиксом в слове cat, может показаться разумным использовать срезы списков. Однако срезы подвержены ошибкам, и вам придётся явно указать количество символов в префиксе или суффиксе. А кому-то, менее знакомому с данным синтаксисом, вообще будут непонятны ваши намерения:

# Не рекомендуется
if word[:3] == 'cat':
    print('Слово начинается на "cat"')

Это не так удобно, как использование .startswith():

# Рекомендуется
if word.startswith('cat'):
    print('Слово начинается на "cat"')

Тот же принцип применяется, когда вы проверяете последние элементы. В приведенном ниже примере показано, как можно проверить, заканчивается ли строка на jpg:

# Не рекомендуется
if file_name[-3:] == 'jpg':
    print('Файл имеет формат JPEG')

Хотя результат правильный, обозначения немного неуклюжи и их трудно читать. Вместо этого можно использовать .endswith(), как в примере ниже:

# Рекомендуется
if file_name.endswith('jpg'):
    print('Файл имеет формат JPEG')

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

Заключение

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

Из этой серии статей вы узнали:

  • что такое PEP 8 и почему он существует;
  • почему вы должны стремиться писать код согласно PEP 8;
  • как написать код, соответствующий PEP 8.

Вдобавок ко всему, вы также увидели, как использовать линтеры и автоформаттеры для проверки вашего кода на соответствие рекомендациям PEP 8.

Если вы хотите узнать больше о PEP 8, вы можете прочитать  всю документацию или зайти на pep8.org, который содержит ту же информацию, но вдобавок в хорошо отформатированном виде. В этих документах вы найдете рекомендации PEP 8, которые не были рассмотрены в нашем цикле статей.

Перевод части статьи How to Write Beautiful Python Code With PEP 8.