PEP8

Основы

Python имеет чёткую внутреннюю систему принятия решений о направлениях дальнейшего развития, построенную на PEP — Python Enhancement Proposal. PEP -это документы, в которых, в строго определённой форме, выдвигаются предложения по улучшению языка и одновременно они являются полноценной документацией с обоснованием нововведения. Многие сходятся во мнении, что самым важным из них является PEP8. По крайней мере он является самым известным – тут сомневаться не приходится. Доступен сам PEP8 online на официальном сайте python. Так же можно найти PEP8 на русском. Именно о нём я расскажу в данном уроке.

О чём PEP 8

PEP8 — это свод рекомендаций по оформлению кода. Почему это важно? Дело в том, что оформление кода сильно влияет на читаемость, а каждый программист знает, что гораздо чаще читают, чем пишут.

PEP8 – является ключевым по тому, что создаёт общий для всех питонистов стандарт оформления кода Python, а значит, большинство программ будет написано так, как все привыкли и их будет легче читать. Что не менее важно, на PEP8 ориентируются всевозможные IDE, линтеры, pep8 checker -ы и т. д.

К созданию данного документа приложил руку непосредственно создатель языка – Гвидо ван Россум.

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

Зачем нужен PEP 8

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

Так же читаемость важна, когда Вы читаете свой собственный код, написанный ранее. Ни для кого не секрет, что программисты стремятся писать что-то новое только один раз, а потом переиспользовать уже написанное когда-то. Это сильно повышает продуктивность. Сможете ли Вы разобраться в своём коде, написанном год назад?

Именно для смягчения перечисленных затруднений и создан PEP8. В нём описан стандарт оформления кода, направленный на то, чтоб все «говорили на одном языке». Он вобрал в себя опыт матёрых разработчиков, которые знают, какой стиль оформления наиболее прост для восприятия.

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

Внешний вид кода

Отступы

Строки, которые состоят из большего числа символов, чем рекомендуется (об этом позже), следует разбивать на несколько строк. Если в такой строке есть скобки (круглые, квадратные или фигурные), то части строки, которые переносятся, должны начинаться на уровне открывающей скобки. Так:

def telegram_sender(message):
    requests.get('{}{}/sendMessage?chat_id={}'
                 '&text={message}'.format(settings_local.URL,
                                          settings_local.TOKEN,
                                          settings_local.CHAT_ID,
                                          message=message))


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

def telegram_sender(message):
    requests.get(
            '{}{}/sendMessage?chat_id={}'
            '&text={message}'.format(
                              settings_local.URL,
                              settings_local.TOKEN,
                              settings_local.CHAT_ID,
                              message=message))


Закрывающую скобку можно переносить на отдельную строку, но отступ должен сохраняться. Делать ли это, остаётся на усмотрение разработчика.

def telegram_sender(message):
    requests.get(
            '{}{}/sendMessage?chat_id={}'
            '&text={message}'.format(
                             settings_local.URL,
                             settings_local.TOKEN,
                             settings_local.CHAT_ID,
                             message=message
                             ))


Либо скобку можно оставлять в начале разбитой строки:

def telegram_sender(message):
    requests.get(
            '{}{}/sendMessage?chat_id={}'
            '&text={message}'.format(
                             settings_local.URL,
                             settings_local.TOKEN,
                             settings_local.CHAT_ID,
                             message=message
    ))

Делать ли это и каким способом, остаётся на усмотрение разработчика.

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

RESHENIA_SELECTOR = 'body > main > section:nth-child(2) > ' \
                    'div > div > div.col.col' \
                    '--xs-12.col--md-8.col--lg-9 > ' \
                    'article > ul > p:nth-child(1) ' \
                    '> a > span > span:nth-child(2) > em'


Отступы у переносимых строк должны быть одинаковыми. Оборачивание в скобки разбиваемой строки предпочтительнее чем использовать обратную косую черту для переноса, как в последнем примере. Всё же в некоторых ситуациях применение обратной косой черты допустимо. Это касается языковых конструкций, в которых нельзя сделать по-другому. К примеру, конструкция with не может использовать неявные продолжения, как и assert:

assert 'очень длинная'
       'строка'
# Вывод:

File "C:\Users\Dushenko\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch.py", line 2

    'строка'

IndentationError: unexpected indent

 

Process finished with exit code 1

Примеры неправильного оформления:

# Аргументы на первой линии запрещены, если не используется вертикальное выравнивание
def telegram_sender(message):
    requests.get('{}{}/sendMessage?chat_id={}'
            '&text={message}'.format(settings_local.URL,
                             settings_local.TOKEN,
                             settings_local.CHAT_ID,
                             message=message))

Совпадение отступов с первым уровнем вложенности
RESHENIA_SELECTOR = 'body > main > section:nth-child(2) > ' \
'div > div > div.col.col' \
'--xs-12.col--md-8.col--lg-9 > ' \
'article > ul > p:nth-child(1) ' \
'> a > span > span:nth-child(2) > em'

Табуляция или пробелы?

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

Использовать табуляции можно только если проект уже начат с таким стилем отступов. Смешивать табуляции и пробелы запрещено.

Максимальная длина строки

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

Для многострочных текстовых блоков, к примеру, таких как docstrings или комментарии, длина не должна превышать 72 символа.

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

Пустые строки

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

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

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

Если Вы правильно используете пустые строки, это может ощутимо повысить читаемость Вашего кода и помочь читающему определить, что этот код делает.

Кодировка исходного файла

Кодировка Python должна быть UTF-8.

Файлы в UTF-8 не должны иметь объявления кодировки.

Импорты

Рекомендуется располагать каждый импорт на отдельной строке

Правильно:


Неправильно:

Однако, если из модуля импортируется несколько сущностей, то их следует перечислить в одной строке:

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

Между импортами и объявлением констант описывается спецификация __all__.

Абсолютное импортирование предпочтительнее относительного (помните, один из главных принципов Пайтон гласит: явное лучше, чем неявное).


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

Никогда. НИКОГДА не делайте так:

Такая конструкция создаёт массу проблем. Подробнее об этом и об импортах вообще читайте в нашей статье Import Python 

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

Не ставьте пробелы в следующих местах:

Между круглыми, квадратными или фигурными скобками и ближайшими к ним символами.

Правильно:

print('Привет землянам' + f' от {alien_species}')


Неправильно:

print( 'Привет землянам' + f' от {alien_species}' )


Перед запятой, точкой с запятой или двоеточием:

Правильно:


Неправильно:

Между именем функции, класса или метода и открывающей скобкой, после которой перечисляются аргументы:

Правильно:


Неправильно:

Перед скобкой, после которой указывается индекс, ключ или срез:

Правильно:


Неправильно:

Применение более, чем одного пробела перед оператором:

Правильно:

переменная_1 = 1
переменная_2 = 2
переменная_с_длинным_именем = 3


Неправильно:

переменная_1                = 1
переменная_2                = 2
переменная_с_длинным_именем = 3

Другие рекомендации

Перед бинарными операторами и после них всегда следует ставить. Вот их список:

присваивания (=, +=, -=, *=, /=, %=, //=), сравнения (==, <, >, !=, <>, <=, >=, in, not in, is, is not), логические (and, or, not).

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

def foo(r, pi=3.14):
    return P(radius=r, pi=pi)


Неправильно:

def foo(r, pi = 3.14):
    return P(radius = r, pi = pi)


Не стоит вкладывать инструкции друг в друга.

Правильно:


Неправильно:

Здесь стоит оговориться, что так всё же можно писать в тех случаях, когда Вы пишите в консоли.

Нельзя писать в одну строку условные операторы и циклы если в они содержат более одной инструкции.

Комментарии

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

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

Точка в конце не ставится только если предложение короткое.

Комментарий должен оканчиваться двумя пробелами.

Комментарии должны быть написаны на английском языке.

Блоки комментариев

Блок комментариев обязан иметь тот же отступ, что и код, идущий после него. В начале каждой строки необходимо ставить символ «#» и пробел после него. Если в блоке есть разделение на абзацы, то они отделяются пустой строкой, но в её начале так же должен стоять символ «#».

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

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

В комментариях нельзя писать очевидные вещи. Так же считается дурным тоном шутить – для этого есть тематические форумы, а код – это дело серьёзное. Плохой пример:

if a:  # Если а не ложно
    pass  # То ничего не делаем


Ещё один плохой пример:

print(str(map(lambda x: x ^ 2, [i / 3 for i in a])))  # Вот это я загнул)))

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

Строки документации должны быть во всех функциях, классах, модулях, пакетах. Необязательны они только в приватных методах.

В PEP 257 отдельно описывается хороший стиль написания docstrings. Закрывающие кавычки должны быть отделены от текста одним вертикальным пробелом.

Пример:

def foo():
    """
    Это функция - пример
    и она ничего не делает
    :return: None

    """
    pass


Но это правило не стоит применять к однострочным документациям.

Пример:

def foo():
    """ Это функция - пример """
    pass


Узнать о комментариях больше Вы можете в уроке комментарии в Python.

Соглашения по именованию

Именование – всегда непростой вопрос. И в Питоне это тоже так. Ниже будут приведены общие рекомендации.

Главный принцип

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

Какие стили имён бывают

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

Стили бывают следующих видов:

  • r — (строчная буква)
  • R — (заглавная буква)
  • lowercase — (слово в нижнем регистре)
  • lower_case_with_underscores — (слова из маленьких букв с нижними подчеркиваниями, «змеиный стиль»)
  • UPPERCASE — (заглавные буквы)
  • UPPERCASE_WITH_UNDERSCORES — (слова из заглавных букв с нижними подчеркиваниями)
  • CapitalizedWords — (слова с заглавными буквами, «верблюжий стиль»). Замечание: когда вы используете аббревиатуры в таком стиле, пишите все буквы аббревиатуры заглавными — HTTPServerError лучше, чем HttpServerError.
  • mixedCase — (отличается от верблюжьего стиля тем, что первое слово начинается с маленькой буквы, часто встречается в других языках программирования)
  • Capitalized_Words_With_Underscores — (слова с заглавными буквами и нижними подчеркиваниями)

st_mode – здесь st – префикс, означающий что поле имеет отношение к системным вызовам POSIX. Этот стиль называется «венгерская нотация» и в Python почти не используется.

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

  • _var — слабый признак того, что это имя приватной сущности. Например, from module import * не будет импортировать сущности, чьи имена начинаются с одинарного символа нижнего подчеркивания.
  • var_ — применяют по соглашению для того чтобы предотвратить конфликт с зарезервированными словами Пайтона, например:
  • __var: — сильный признак того, что это имя приватной сущности. Он изменяет имя атрибута класса чтоб усложнить его нежелательное использование. Пример:

from pprint import pprint

class foo():
    __myattr = 'приватный'

pprint(dir(foo))
# Вывод:

['__class__',

'__delattr__',

'__dict__',

'__dir__',

'__doc__',

'__eq__',

'__format__',

'__ge__',

'__getattribute__',

'__gt__',

'__hash__',

'__init__',

'__init_subclass__',

'__le__',

'__lt__',

'__module__',

'__ne__',

'__new__',

'__reduce__',

'__reduce_ex__',

'__repr__',

'__setattr__',

'__sizeof__',

'__str__',

'__subclasshook__',

'__weakref__',

'_foo__myattr']

Здесь, как Вы видите, атрибут __myattr автоматически превратился в _foo__myattr.

  • __var__ — (двойное нижнее подчеркивание вокруг имени): магические или «дандер» методы или атрибуты, расположенные в пространствах имен, которыми может управлять пользователь. Например, __name__, __init__ или __call Подобный стиль использовать не стоит.

Имена, которые не стоит использовать

Избегайте использования в именах одиночных символов, которые похожи на цифры. Это l (строчная латинская буква «эль»), O (заглавная латинская буква «о») и I (заглавная латинская буква «ай»).

Имена модулей и пакетов

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

Имена классов

Имена классов должны обычно соответствовать верблюжьему стилю.

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

Имена исключений

Поскольку исключения – это тоже классы, к исключениям тоже применяется верблюжий стиль именования.

Имена функций

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

Имена глобальных переменных

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

Есть нюанс, заслуживающий отдельного внимания. При импорте модуля при помощи конструкции from module import * импортируются все глобальные переменные. Как правило, это нежелательное поведение, поскольку такие переменные являются приватными по отношению к модулю. Чтобы этого избежать можно использовать описанный выше трюк с одиночным нижним подчёркиванием в начале имени, но такими переменными неприятно оперировать внутри модуля. Есть другой способ – дандер атрибут __all__. Если он указан в модуле, то при его импорте конструкцией from module import * будут импортированы только перечисленные в атрибуте __all__ сущности.

Аргументы функций и методов

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

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

Если имя параметра конфликтует с зарезервированным ключевым словом Питона и Вы не можете подобрать синоним, следует добавить в конец имени одиночное нижнее подчеркивание, чем исказить написание имени или использовать аббревиатуру. В итоге, имя type_ лучше, чем имя tp.

Имена методов и переменных экземпляров классов

Для методов и переменных экземпляров классов действуют те же правила, что и для функций – используем змеиный стиль.

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

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

Как уже говорилось, Python искажает такие имена. По сути, это просто синтаксический сахар. Посмотрите на этот короткий пример:

class foo():
    _foo__myattr = 'приватный'
    __myattr = 'ещё_один_приватный'

print(foo._foo__myattr)
# Вывод:

ещё_один_приватный


Здесь мы получили в выводе «ещё_один_приватный» по тому, что __myattr и _foo__myattr – это одно и то же и мы просто переопределили значение атрибута.

Константы

Константы записывают заглавными буквами. Если имя константы состоит из нескольких слов, они разделяются одиночными нижними подчёркиваниями. Перечисляются константы (и инициализируются – заполняются значениями) в начале скрипта, сразу после спецификации __all__.

Проектирование наследования

Проблема уровней доступа не возникает, пока Вы пишете код самостоятельно. Но, представьте себе код, над которым работает несколько команд одновременно. На основе того, что Вы написали, пишут код другие разработчики. Теперь, если Вы что-то измените в своём коде, это может вызвать проблемы на другом конце проекта. Значит придётся поддерживать обратную совместимость – то, что написано однажды, должно существовать и дальше. Результатом такого положения дел, скорее всего, будет обрастание проекта «легаси» кодом. Это такой код, который работает, но от него все хотели бы избавиться. Но никто не может, так как этот кирпичик лежит в самом основании.

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

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

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

Перейдём к рекомендациям:

  • Публичные атрибуты (входящие в интерфейс) не должны иметь в начале имени знака нижнего подчеркивания.
  • Если наименование публичного атрибута создаёт конфликт с зарезервированным ключевым словом Питона, добавьте последним символом имени один знак нижнего подчеркивания.
  • Публичные атрибуты должны быть простыми и называться понятными именами. И не пишите сложные геттеры и сеттеры. Их легко добавить потом, если потребуется, при помощи свойств (properties).
  • Если Вы планируете класс таким образом, чтобы от него наследовались другие классы, но не хотите, чтобы подклассы унаследовали некоторые атрибуты, добавьте в имена два символа нижнего подчеркивания в начало, и ни одного — в конец. Таким образом Вы сделаете их приватными.

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

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

Общие рекомендации

  • Код должен быть универсальным и написан таким образом, чтобы не зависеть от конкретной реализации языка (PyPy, Brython, Jython, IronPython, Nuitka, Cython и пр.).
  • Сравнения с None, как и во многих других языках программирования, должны непременно быть выполнены с применением операторов is или is not, но не с использованием операторов сравнения. Так же, вместо if x, следует писать if x is not None так как в некоторых случаях приведения типов значение None может быть приведено к значению False.
  • При реализации методов сравнения, лучше всего реализовать все 6 операций сравнения (__eq__, __ne__, __lt__, __le__, __gt__, __ge__), чем рассчитывать на то, что другие разработчики будут применять только определённый вид сравнения.

Вы можете использовать декоратор total_ordering() из модуля functools стандартной библиотеки для реализации недостающих методов.

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

Правильно:


Неправильно:

  • При создании пользовательских классов исключений их следует наследовать от класса Exception, а не от BaseException. Прямое наследование от BaseException зарезервировано для исключений, которые не следует перехватывать. К примеру, это SystemExit и KeyboardInterrupt.
  • Python 3, «raise SomeError from OtherError» следует использовать для указания явной подмены исключения без потери отладочной информации.
  • Когда код перехватывает исключения в блоках try — except, перехватывайте конкретные ошибки.

Правильно:

def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError as ex:
        raise ValueError('b must not be zero')
divide(10, 0)
# Вывод:

Traceback (most recent call last):

File "C:\Users\Dushenko\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch.py", line 3, in divide

        return a / b

ZeroDivisionError: division by zero

 

During handling of the above exception, another exception occurred:

 

Traceback (most recent call last):

File "C:\Users\Dushenko\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch.py", line 6, in <module>

divide(10, 0)

File "C:\Users\Dushenko\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch.py", line 5, in divide

        raise ValueError('b must not be zero')

ValueError: b must not be zero

 

Process finished with exit code 1


Неправильно:

def divide(a, b):
    try:
        return a / b
    except Exception as ex:
        raise ValueError('b must not be zero')
divide(10, '1')
# Вывод:

Traceback (most recent call last):

File "C:\Users\Dushenko\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch.py", line 3, in divide

        return a / b

TypeError: unsupported operand type(s) for /: 'int' and 'str'

 

During handling of the above exception, another exception occurred:

 

Traceback (most recent call last):

File "C:\Users\Dushenko\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch.py", line 6, in <module>

divide(10, '1')

File "C:\Users\Dushenko\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch.py", line 5, in divide

        raise ValueError('b must not be zero')

ValueError: b must not be zero

 

Process finished with exit code 1


Существуют и исключения из этого правила:

  1. Если обработчик выводит полную информацию о возникшем исключении.
  2. Если нужно выполнить какие-то действия после перехвата исключения, а потом вызвать его же в целях обработки в другом месте кода. Однако, всё же правильнее применять конструкцию «try-finally».
  • Надо стремиться оборачивать в конструкцию try-except как можно меньше кода, чтобы проще было перехватывать исключения.

Подробнее об исключениях Вы можете узнать в нашем уроке Try Except в Python

  • Когда какой-либо ресурс является локальным на определённом участке кода, применяйте выражение withдля того, чтобы после выполнения всех необходимых операций он был надёжно и оперативно очищен.
  • Используйте строковые методы вместо модуля string — они всегда быстрее и имеют тот же API для unicode-строк.
  • Используйте строковые методы ».startswith() и ».endswith() вместо срезов строк для проверки суффиксов или префиксов.

Правильно:


Неправильно:

  • Сравнение типов объектов нужно делать с помощью isinstance(), а не прямым сравнением типов:

Правильно:


Неправильно:

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

Правильно:


Неправильно:

if len(my_list): pass

if len(my_set) >= 0: pass

  • Не применяйте строковые константы, которые содержат пробелы в конце — такие символы невидимы, а многие среды разработки и редакторы кода обрезают их.
  • Не сравнивайте булевы типы с True и False с помощью двойного символа равно:

Правильно:


Неправильно:

Вообще неправильно:

Избегайте волшебной палочки 

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

  • изменить способ создания объектов
  • изменить способ импорта модулей интерпретатором Python
  • встроить подпрограммы на C.

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

Я наблюдал ситуацию, когда на проекте, в котором участвовали как юниор, так и сеньор разработчики. «Сеньоры» писали код на своём уровне, используя мета программирование, дата классы, каскадное наследование и прочее, хотя, на мой взгляд, всё можно было бы написать с использованием обычных функций и код от этого только выиграл бы. В итоге «юниоры» буксовали в тщетных попытках разобраться в коде.

Пожалуйста, не увлекайтесь фичами. У Пайтона есть определённая философия и её ядром является то, что это язык очень высокого уровня. Это одна из причин его популярности. Язык высокого уровня – что это означает? Это означает, что не надо опускаться к подробностям реализации чего-либо. Пишите декларативно – отдавайте простые и ясные команды. Чаще всего самый простой путь будет самым лучшим. Не гонитесь за скоростью и оптимизацией каждой строки кода – Python «не про это».

Без сомнения, профессиональный разработчик должен знать множество трюков и фич своего языка, ведь изредка бывают ситуации, когда без них не обойтись. Но применять их стоит как можно реже – не убивайте читаемость и даже если у Вас чёрный пояс по Питону, пожалейте тех, кто придёт после Вас.

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

test = [1, 2, 5, 4, 2, 1, 3, 1, 4, 6, 4]
print(max(set(test), key = test.count))
# Вывод:

1

Мы все ответственные пользователи 

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

Хорошим примером этого будет то, что клиентский код может переопределять атрибуты и методы объекта, ведь в Питоне на самом деле нет полноценной инкапсуляции. И это следствие философии Python, которую зачастую сложно воспринять представителям языков с высокой степенью защиты, таких как C++ или Java. Заключается эта философия всего в одной фразе: «Мы все ответственные пользователи».

Это означает, что вся инкапсуляция (и многое другое) держится на соглашениях, принятых в сообществе питонистов. Инкапсулируя атрибут, разработчик лишь говорит коллегам: этот атрибут – приватный, его использовать не стоит, но не прячет его на самом деле. И да, даже атрибут с двумя нижними подчёркиваниями в начале имени, может использовать клиентский код, но, если он будет это делать, он будет это делать на свой страх и риск – в отношении этого атрибута никто ничего не гарантировал, и он может быть изменён «владельцем» в любое время (на то он и приватный). В такой ситуации любое неправильное поведение или проблемы, появившиеся при внесении изменений в код, являются ответственностью клиентского кода. Вас предупредили.

Придерживаться этого соглашения приветствуется: любой метод или свойство, которые не были предназначены для публичного использования (клиентским кодом), должны иметь в начале символ одиночного или двойного нижнего подчеркивания. Это гарантирует лучшее межевание обязанностей и более простое изменение существующего кода.

Автоматическая PEP8 проверка Python-кода

К счастью, у нас есть огромное подспорье в оформлении кода – утилиты автоматической проверки. Сейчас они встроены в любую среду разработки. К примеру, PyCharm проверяет оформление «на лету» и подсвечивает проблемы. Жмём Ctrl + Alt + O и IDE наводит порядок в импортах. Нажимаем Ctrl + Alt + L и PyCharm сам форматирует код. А если необходимо выполнить работу, а среды разработки нет под рукой? На GitHub представлен целый раздел Python Code Quality Authority, где располагаются инструменты для повышения качества кода, в том числе инструменты для проверки стиля оформления на соответствие PEP 8: flake8, pep8-naming, pycodestyle.

Вот небольшой список утилит для помощи в форматировании кода:

pep8 – рекурсивно просматривает все файлы в директориях на соответствие оформления кода стандарту pep8

autopep8 — как и pep8, данная утилита может самостоятельно выявлять ошибки, а также исправлять их

autoflake — утилита помогает удалить импорты и переменные, которые не используются

unify — позволяет автоматически приводить строки в соответствие стандарту PEP 8

docformatter — позволяет автоматически приводить строки документации в соответствие стандарту PEP 8

pyformat – всё перечисленное в одном флаконе

Осознанная необходимость

Не забывайте, что знать PEP 8 Вы, как разработчик, должны, а следовать ему — не обязаны. Вы вынуждены соблюдать правила, касающиеся отступов, так как в противном случае интерпретатор скажет Вам: IndentationError: unexpected indent. Но в самом PEP 8 перечислены случаи, когда программист по своему усмотрению может и должен не выполнять рекомендации.

Чёткие ограничения действуют только для публичных проектов-библиотек.

Когда лучше проигнорировать PEP8

Чаще всего применять PEP8 очень даже стоит. Если вы безукоризненно исполняете все рекомендации PEP8, можно с уверенностью гарантировать «чистоту», высокий уровень читаемости кода и профессионализм программиста. Это принесет пользу всем соприкасающимся с Вашим кодом, от коллег до конечного заказчика продукта. Но все же некоторые рекомендации PEP8 неприменимы в следующих случаях:

  1. Когда соблюдение PEP8 нарушит совместимость с существующим программным обеспечением;
  2. Когда код, сопутствующий тому, над чем вы работаете, несовместим с PEP8;
  3. Когда код нужно оставить совместимым с неактуальными версиями Python.

Главной же причиной, по которой не стоит применять PEP8 являются стандарты оформления кода, принятые на проекте, но отличные от рекомендуемых PEP8. Чаще всего это бывает из-за того, что основной стек предприятия располагается вокруг другого языка и всё подгоняется под его стандарты в целях достижения единообразия. Не беда! Лишь бы всем было удобно. Не забывайте, что PEP8 – это всего лишь рекомендации.

 

Оцените статью
О Python на русском языке
Добавить комментарий

Adblock
detector