Питонический путь
Комментирование – это скучно.
Документирование отнимает время.
Test2 – хорошее имя для класса.
a_final – хорошее имя для переменной.
Мне не нужно писать тесты, эта функция не так важна.
Это лишь несколько ложных утверждений, которые могут привести к разработке некачественного кода.
Разработка плохого кода, безусловно, быстрее и проще, чем создание удобного и поддерживаемого кода. Но какой ценой?
Как создать код, который легко читать и поддерживать?
Как избежать многочасового исправления кода, разработанного коллегой, который ушел из компании 6 месяцев назад?
В приведенных ниже правилах заключены наиболее известные лучшие практики разработки чистого кода.
Документирование кода Python
Документация важна как для вас, так и для других.
Прежде чем приступить к написанию новых кодов, вы должны спросить себя:
- Как я могу сделать ее более понятной?
- Как я смогу понять эту функцию через 6 месяцев?
Это руководство охватывает все основные принципы, позволяющие легко выбрать и принять один из самых известных стилей документирования:
Комментирование кода Python
Основная цель комментирования кода – повысить читабельность.
Комментарии следует задавать только в том случае, если они добавляют в код дополнительную информацию.
hello_world = 'hello world' # assign value 'hello world' to hello_world
Как легко видеть, комментарий к коду выше не нужен, потому что он не добавляет никакой ценности.
Рекомендации по стилю
Прежде чем написать первый класс или метод в новом проекте, встретьтесь со всей командой разработчиков, чтобы определить стандартный синтаксис, который будет принят.
Ниже приведены примеры стилей, взятые из моего личного опыта:
- CapWords: этот стиль обычно используется для классов.
- CAPS_WITH_UNDERSCORE: обычно используется для статических переменных
- snake_case: это принято для обозначения методов и переменных
Классы Python
Хорошее имя класса никогда не состоит из глаголов и всегда должно состоять из имен существительных или имен существительных (например, Employee, TeamLead).
Ниже приведен пример класса Employee, дополненный документацией и методом init для инициализации экземпляра.
class Employee(object):
"""The summary line for a class docstring should fit on one line.
If the class has public attributes, they may be documented here
in an ``Attributes`` section and follow the same formatting as a
function's ``Args`` section. Alternatively, attributes may be documented
inline with the attribute's declaration (see __init__ method below).
Properties created with the ``@property`` decorator should be documented
in the property's getter method.
Attributes:
attr1 (str): Description of `attr1`.
attr2 (:obj:`int`, optional): Description of `attr2`.
"""
def __init__(self, param1, param2, param3):
"""Example of docstring on the __init__ method.
The __init__ method may be documented in either the class level
docstring, or as a docstring on the __init__ method itself.
Either form is acceptable, but the two should not be mixed. Choose one
convention to document the __init__ method and be consistent with it.
Note:
Do not include the `self` parameter in the ``Args`` section.
Args:
param1 (str): Description of `param1`.
param2 (:obj:`int`, optional): Description of `param2`. Multiple
lines are supported.
param3 (:obj:`list` of :obj:`str`): Description of `param3`.
Функции Python
ФУНКЦИИ ДОЛЖНЫ ДЕЛАТЬ ОДНО ДЕЛО.
ОНИ ДОЛЖНЫ ДЕЛАТЬ ЭТО ХОРОШО.
ОНИ ДОЛЖНЫ ДЕЛАТЬ ТОЛЬКО ЭТО.
В отличие от классов, функции (и методы) определяются, начиная с глаголов, таких как:
- get_name(…)
- set_name(…)
Каждая функция должна делать что-то одно, в противном случае разбейте ее на несколько функций.
ФУНКЦИИ ДОЛЖНЫ ЛИБО ЧТО-ТО ДЕЛАТЬ, ЛИБО ЧТО-ТО ОТВЕЧАТЬ, НО НЕ ТО И ДРУГОЕ.
Не передавайте в функцию слишком много параметров, более трех параметров должны требовать особого обоснования.
def create_circle(
x: float,
y : float,
radius : float) -> Circle:
"""This method returns a Circle instance
Args:
x (float): value on the x-axis
y (float): value on the y-axis
radius (float): radius of the Circle
Returns:
circle (Circle): circle instance
"""
Поэтому, когда функция требует более трех аргументов, некоторые из них можно обернуть в отдельный класс.
Приведенный ниже пример показывает простой способ обернуть две переменные в один класс. Кроме того, такой код легче читать.
class Point(object):
"""This class is responsible for creating a Point from x and y values.
Attributes:
x (float): x value on x-axis
y (float): y value on y-axis
"""
def __init__(self, x : float, y : float):
"""Init of circle instance
Args:
x (float): x value on x-axis
y (float): y value on y-axis
"""
self.x = x
self.y = y
def create_circle(
center: Point,
radius : float) -> Circle:
"""This method returns a Circle instance
Args:
center(Point): center of the circle
radius (float): radius of the Circle
Returns:
circle (Circle): circle instance
"""
В конечном итоге, каждая функция никогда не должна возвращать слишком много объектов, это может привести к искажению ее основной цели.
Блок Try/Except
Ошибки в Python могут быть двух типов – синтаксические ошибки и исключения.
- синтаксические ошибки: возникают при ошибках в использовании языка Python
- исключения: возникают при возникновении некоторых событий, которые изменяют нормальный ход программы и относятся к выполнению программы.
Для обработки этих типов событий наиболее распространенным способом является использование блока try/except, где:
- блок try содержит логику функции, от которой разработчики ожидают правильного выполнения
- блок except выполняет некоторый код всякий раз, когда в блоке try возникают ошибки
- блок finally выполняет код после блоков try и except
try:
# some code
except Exception as err:
# handling exceptions
finally:
# some code
Тестирование кода
БЕЗ ТЕСТОВ КАЖДОЕ ИЗМЕНЕНИЕ – ЭТО ВОЗМОЖНАЯ ОШИБКА
Тестирование кода имеет фундаментальное значение для того, чтобы всегда соответствовать поведению компонента.
Допустим, команде разработчиков необходимо добавить новые функции в существующий проект.
Первый шаг, который они должны сделать, – разбить эти функции на несколько функций и определить их поведение. Затем эти модели поведения должны тестироваться каждый раз, когда в проект вносятся новые изменения.
Поскольку проект может состоять из различных компонентов, как они могут знать, что текущие изменения не повлияют на другие части проекта?
Следуя принципу, что одна функция должна выполнять одну задачу, команда должна создать один модульный тест для каждой функции
def test_sum():
"""This test checks the behavior of sum method
"""
assert sum([1, 2, 3]) == 6, "Should be 6"