26 декабря 2022

🐍🧹 3 принципа написания чистого кода на Python

iOS-developer, ИТ-переводчица, пишу статьи и гайды.
Даже опытные программисты с трудом пишут чистый код, и это часто ощущается как постоянная битва за то, чтобы все было аккуратно и упорядочено. В статье на примерах показываем, как писать чистый и осмысленный код и как правильно оформлять документацию.
🐍🧹 3 принципа написания чистого кода на Python
Данная статья является переводом. Автор: Youssef Hosni. Ссылка на оригинал.

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

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

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

1. Характеристики высококачественного кода

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

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

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

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

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

  • Узконаправленный код: Каждая функция, класс или модуль должны делать одну вещь и делать это хорошо.
  • Легко читаемый код: По словам Грэди Буча, автора книги «Объектно-ориентированный анализ и проектирование с примерами приложений», чистый код читается как хорошо написанная проза.
  • Простота отладки кода: Чистый код можно легко отлаживать и исправлять ошибки, поскольку он легко читаем и выполняем.
  • Простота сопровождения: Другие разработчики могут легко читать и улучшать его.

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

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

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

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека программиста»

2. Присвоение имён

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

Во-первых, давайте начнем с соглашений об именах PEP 8:

  • имена классов должны быть CamelCase (стиль написания, при котором несколько слов пишутся слитно без пробелов, каждое слово при этом пишется с прописной буквы) (MyClass);
  • имена переменных должны быть snake_case (стиль написания, при котором все слова пишутся в нижнем регистре и разделяются нижним подчеркиванием) (first_name);
  • имена функций должны быть snake_case и в нижнем регистре (quick_sort());
  • константы должны быть snake_case и все в верхнем регистре (PI = 3.14159);
  • модули должны иметь короткие имена в snake_case и все строчные буквы (numpy);
  • одинарные и двойные кавычки обрабатываются одинаково (просто выберите один и будьте последовательны).

Вот более подробное объяснение того, как грамотно присвоить имена:

2.1. Переменные

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

        # Not recommended
# The au variable is the number of active users
au = 105

# Recommended 
total_active_users = 105
    

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

        # Not recommended
c = [“UK”, “USA”, “UAE”]

for x in c:
print(x)

# Recommended
cities_list = [“UK”, “USA”, “UAE”]
    for city in cities_list:
        print(city)
    

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

        # Not recommended
client_first_name = ‘John’
customer_last_name = ‘Doe;

# Recommended
client_first_name = ‘John’
client_last_name = ‘Doe’

# Another example:

# bad code
def fetch_clients(response, variable):
    # do something
    pass

def fetch_posts(res, var):
    # do something
    pass

# Recommended
def fetch_clients(response, variable):
    # do something
    pass

def fetch_posts(response, variable):
    # do something
    pass
    

4. Не используйте магические числа. Магические числа — это числа со специальной жестко заданной семантикой, которые появляются в коде, но не имеют никакого значения или объяснения. Обычно эти числа появляются как литералы более чем в одном месте кода.

        import random

# Not recommended
def roll_dice():
    return random.randint(0, 4)  # what is 4 supposed to represent?

# Recommended
DICE_SIDES = 4

def roll_dice():
    return random.randint(0, DICE_SIDES)
    

2.2. Функции

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

        DICE_SIDES = 4

# Not recommended
def roll_dice_using_randint():
    return random.randint(0, DICE_SIDES)  
    
# Recommended
def roll_dice():
    return random.randint(0, DICE_SIDES)
    

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

        # Not recommended
def fetch_user(id): 
    # do something
    Pass

def get_post(id):
    # do something
    pass

# Recommended
def fetch_user(id): 
    # do something
    Pass


def fetch_post(id):
    # do something
    pass
    

7. Не используйте флаги, в том числе логические. Логические флаги — это переменные, которые содержат логическое значение — true или false. Эти флаги передаются функции и используются функцией для определения ее поведения.

        text = "Python is a simple and elegant programming language."

# Not recommended
def transform_text(text, uppercase):
    if uppercase:
        return text.upper()
    else:
        return text.lower()

uppercase_text = transform_text(text, True)
lowercase_text = transform_text(text, False)


# Recommended
def transform_to_uppercase(text):
    return text.upper()

def transform_to_lowercase(text):
    return text.lower()

uppercase_text = transform_to_uppercase(text)
lowercase_text = transform_to_lowercase(text)
    

2.3. Классы

8. Не добавляйте лишний контекст. Это может произойти из-за добавления ненужных переменных к именам переменных при работе с классами.

        # Not recommended
class Person:
    def __init__(self, person_username, person_email, person_phone, person_address):
        self.person_username = person_username
        self.person_email = person_email
        self.person_phone = person_phone
        self.person_address = person_address

# Recommended
class Person:
    def __init__(self, username, email, phone, address):

        self.username = username
        self.email = email
        self.phone = phone
        self.address = address
    

3. Использование пустого пространства

3.1. Отступ

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

        # Correct:

# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# Hanging indents should add a level.
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)
    
        # Wrong:

# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# Further indentation required as indentation is not distinguishable.
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)
    

3.2. Максимальная длина линии

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

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

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

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

4. Комментарии и документация

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

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

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

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

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

Вот несколько советов, как писать хорошие комментарии:

1. Не комментируйте плохой код, перепишите его

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

2. Не добавляйте комментарии, когда в этом нет необходимости.

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

        # This checks if the user with the given ID doesn't exist.
if not User.objects.filter(id=user_id).exists():
    return Response({
        'detail': 'The user with this ID does not exist.',
    })
    

Как правило, если вам нужно добавить комментарии, они должны объяснять, почему вы что-то сделали, а не то, что происходит.

3. Не оставляйте закомментированный устаревший код

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

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

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

Первая строка docstrings представляет собой краткое объяснение назначения функции. Следующий элемент строки документации — это объяснение аргументов функции. Здесь вы перечисляете аргументы, указываете их назначение и типы. Наконец, обычно приводится некоторое описание вывода функции. Каждая часть строки документации является необязательной; однако строки документа являются частью хорошей практики кодирования. Ниже приведены два примера строки документации для функции. В первом будет использоваться однострочная строка документации, а во втором — многострочные строки документации:

        def population_density(population, land_area):
    """Calculate the population density of an area."""
    return population / land_area
    
        def population_density(population, land_area):
    """Calculate the population density of an area.

    Args:
    population: int. The population of the area
    land_area: int or float. This function is unit-agnostic, if you pass in values in terms of square km or square miles the function will return a density in those units.

    Returns:
    population_density: population/land_area. The population density of a 
    particular area.
    """
    return population / land_area
    

4.3. Документация

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

Отличным первым шагом в проектной документации является файл README. Очень часто это будет первым взаимодействием большинства пользователей с вашим проектом. Будь то приложение или пакет, к вашему проекту обязательно должен быть приложен файл README. Как минимум он должен содержать объяснения того, что он делает, и перечисления зависимостей, а также предоставлять достаточно подробные инструкции о том, как его использовать. Это поможет другим понять цель вашего проекта и быстро получить что-то работающее.

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

***

Материалы по теме

Источники

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ