
Работа программиста требует создания максимально удобочитаемого кода. Это означает выполнение следующих требований (привожу далеко не полный список):
- имена переменных, как и имена функций, должны быть осмысленными и носить описательный характер (вместо a, b и c);
- код должен сопровождаться необходимыми пояснительными комментариями и документацией;
- повсеместное использование аннотаций типов обязательно;
- строки должны быть настолько длинными и многословными, насколько это необходимо для их понимания.
Выполнение вышеприведенных требований неизбежно приводит к развитию стилевого оформления кода.
Итак, вот несколько стилей продакшн-кода на Python, которые мне удалось выработать за последние несколько лет.
1. Распаковка кортежа с использованием скобок
Вот пример обычной распаковки кортежей:

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

Обратите внимание: этот стиль распаковки кортежей позволяет вмещать гораздо более длинные (и более описательные) имена переменных.
Более реалистичный пример:

2. Списковое выражение в нескольких строках
Вот как выглядит обычное списковое выражение:

В продакшн-коде не может использоваться имя переменной i — оно должно быть гораздо длиннее и описательнее. Но тогда все списковое выражение невозможно уместить в одной строке кода.
Я бы переписал приведенный выше код так:

Более реалистичный пример:

3. Объединение строк с помощью скобок
Строки кода уровня продакшн обычно не умещаются в одну строку из-за свойственной им многословности. Поэтому следует объединить их с помощью скобок.

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

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

Обратите внимание: запись цепочки методов внутри скобок избавляет от необходимости использовать символ \ для явного перевода строк.
5. Индексирование вложенных словарей
Вот обычный способ индексирования вложенных словарей:

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

Если же этого недостаточно, разбиваем код индексации на большее количество строк:

А если его все еще трудно читать, можно сделать вот что:

6. Написание удобочитаемых и информативных функций
Вчерашние студенты обычно так пишут функции:

PR, содержащие такой код, скорее всего, будут отклонены, поскольку:
- имя функции не является описательным;
- неинформативные имена переменных параметров;
- нет аннотаций типов, поэтому сразу невозможно определить, к какому типу данных должен относиться каждый параметр;
- из-за отсутствия аннотаций типов трудно понять, что должна возвращать функция;
- нет документации кода (docstring), поэтому приходится догадываться, что делает функция.
А вот как пишутся функции в продакшн-коде на Python:

Этот код отличается:
- описательным именем функции;
- описательными именами параметров (не просто a, b, c);
- наличием аннотации типа у каждого параметра;
- включением типа возврата функции;
- наличием docstring (документации, представленной в виде строки в тройных кавычках) с подробным описанием того, что делает функция, какие параметры она принимает и что выдает.
7. Максимально возможное сокращение уровней отступов
Вот цикл for. Если условие соблюдено, можно что-то выполнить (do something).

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

Обратите внимание: отступ для do_something() уменьшился на 1 уровень только за счет использования if not condition вместо if condition.
В продакшн-коде уровней отступов может быть гораздо больше, и если их слишком много, код становится нечитабельным и раздражающим. Приведенный выше лайфхак позволяет сделать его более аккуратным и удобным для чтения.
8. Булевы условия со скобками
Вот оператор if с тремя условиями, объединенными с помощью ключевого слова and.

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

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

9. Защита от значений None
Вот обычный код, который обращается к некоему вложенному атрибуту объекта:

В этом коде есть проблемы, которые могут привести к отклонению PR:
- если dog (собака) — None, получим ошибку;
- если dog.owner (владелец собаки) — None, также получим ошибку;
- этот блок кода не защищает нас от риска того, что dog или dog.owner могут быть None.
В продакшн-коде необходимо активно защищаться от подобных казусов. Вот как можно переписать этот код:

В Python and и or — операторы, выполняющиеся по короткой схеме. Это значит, что они прекращают оценку всего выражения, как только получают однозначный ответ:
- если dog — None, выражение завершается на «if dog»;
- если dog — не None, а dog.owner — None, выражение завершается на «if dog and dog.owner»;
- если нет никаких значений None, к dog.owner.name (имя владельца собаки) будет получен успешный доступ для сравнения со строкой «bob».
Таким образом, обеспечивается дополнительная защита от возможности того, что dog или dog.owner могут быть значениями None.
10. Защита от итерации по значениям None
Вот как можно выполнить итерацию по некоторому итерируемому объекту (например, списку, словарю, кортежу и т. д.):

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

Выражение «mylist or None»:
- возвращает mylist, если значение истинно (например, непустой итерируемый объект);
- возвращает [], если значение mylist ложно (например, None или пустой итерируемый объект).
Таким образом, если mylist равен None, выражение «mylist or None» возвращает [ ] и предотвращается возможность столкнуться с нежелательным исключением.
11. Добавление перед внутренними методами символа _
Вот пример класса, где метод run использует другие методы clean и transform:

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

Обратите внимание: добавление подчеркивания перед именем метода не скрывает его от других классов и объектов. На самом деле функционально разницы нет.
12. Использование декораторов, содержащих общую функциональность
Вот класс с 3 функциями, каждая из которых выполняет разные действия. Однако обратите внимание, что в разных функциях есть схожие шаги — блок try-except, а также функциональность логирования.

Хорошей практикой для сокращения количества повторяющегося кода будет написание функции-декоратора, содержащей общую функциональность.

Таким образом, для обновления общего кода (try-except и код логирования) не придется обновлять его в трех местах — нужно обновить только код декоратора, содержащий общую функциональность.
Читайте также:
- Код на Python медленный? Вот 5 простых решений, чтобы быстро его ускорить
- Как быстро создать PDF-файл с помощью Python
- 5 приемов Python, которые отличают профессионалов от новичков
Читайте нас в Telegram, VK и Дзен
Перевод статьи Liu Zuo Lin: 12 Production-Grade Python Code Styles I’ve Picked Up From Work





