June 24, 2022

Чистый код в python

Что такое чистый код?

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

PEP 8 - Python Enhancement Proposal(предложение по улучшению)

Соглашения об именах PEP 8:

  • имена классов должны быть CamelCase( MyClass)
  • имена переменных должны быть в snake_case в нижнем регистре ( first_name)
  • имена функций должны быть в snake_case в нижнем регистре ( quick_sort())
  • константы должны быть snake_case в верхнем регистре ( PI = 3.14159)
  • модули должны иметь короткие имена в snake_case и все строчные буквы ( numpy)
  • одинарные <'> и двойные кавычки <"> обрабатываются одинаково (просто выберите один и будьте последовательны)

Форматирование строки PEP 8:

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

Пробелы PEP 8:

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

Комментарии PEP 8:

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

подробнее о pep8

Pythonic Code ("Питонячий" код)

Pythonic Code — это набор идиом, принятый сообществом Python, чтобы сделать ваш код более чистым, читабельным и высокопроизводительным.

Pythonic Code включает в себя:

  • variable tricks("трюки" с переменными)
  • манипуляции со списками (инициализация, нарезка)
  • работа с функциями
  • явный код

Есть большая разница между обычным кодом Python и "Питонячим" кодом. Чтобы написать Pythonic Code, вы не можете просто идиоматически перевести другой язык (например, Java или C++) на Python; вам нужно думать на Python.

Давайте посмотрим на пример. Мы должны сложить первые 10 чисел вместе вот так 1 + 2 + ... + 10.

Не-Pythonic решение будет примерно таким:

n = 10
sum_all = 0

for i in range(1, n + 1):
    sum_all = sum_all + i

print(sum_all)  # 55

Более Pythonic решение может выглядеть так:

n = 10
sum_all = sum(range(1, n + 1))

print(sum_all)  # 55

Второй пример намного легче прочитать опытному разработчику Python, но он требует более глубокого понимания встроенных функций и синтаксиса Python. Самый простой способ писать Pythonic Code — помнить о принципах Python при написании кода и постепенно изучать стандартную библиотеку Python .

Дзен Python

Zen of Python — это набор из 19 «руководствующих принципов» написания компьютерных программ на Python. Сборник был написан в 1999 году инженером-программистом Тимом Питерсом. Он включен в качестве пасхального яйца в интерпретатор Python.

Вы можете увидеть это, выполнив следующую команду:

import this
>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Если вам интересно значение слова «стихотворение», ознакомьтесь с книгой «Дзен Python, объяснение », в которой дается построчное объяснение.

Принципы

Существует множество принципов написания кода, которым вы можете следовать, чтобы писать более качественный код, каждый из которых имеет свои плюсы/минусы и компромиссы. В этой статье рассматриваются четыре наиболее популярных принципа: DRY, KISS, SoC и SOLID.

DRY (Don't repeat yourself )

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

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

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

KISS (Будь проще, глупый)

Большинство систем работают лучше всего, если они остаются простыми, а не усложняются.

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

SoC (разделение задач)

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

Хорошим примером SoC является MVC (модель — представление — контроллер).

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

SOLID

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

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

Он состоит из следующих пяти понятий:

Форматировщики кода

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

Наиболее популярные средства форматирования кода Python:

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

Самые популярные линтеры Python:

Дополнительные сведения о линтинге и форматировании кода см. в обзоре Python Code Quality .

Соглашения об именах

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

# This is bad
# represents the number of active users
au = 55

# This is good
active_user_amount = 55

Мы рассмотрим дополнительные примеры в следующих двух разделах.

Переменные

1. Используйте существительные для имен переменных

2. Используйте описательные/раскрывающие намерения имена

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

# This is bad
c = 5
d = 12

# This is good
city_counter = 5
elapsed_time_in_days = 12

3. Используйте произносимые имена

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

from datetime import datetime

# This is bad
genyyyymmddhhmmss = datetime.strptime('04/27/95 07:14:22', '%m/%d/%y %H:%M:%S')

# This is good
generation_datetime = datetime.strptime('04/27/95 07:14:22', '%m/%d/%y %H:%M:%S')

4. Избегайте двусмысленных сокращений

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

# This is bad
fna = 'Bob'
cre_tmstp = 1621535852

# This is good
first_name = 'Bob'
creation_timestamp = 1621535852

5. Всегда используйте один и тот же словарь

Избегайте использования синонимов при именовании переменных.

# This is bad
client_first_name = 'Bob'
customer_last_name = 'Smith'

# This is good
client_first_name = 'Bob'
client_last_name = 'Smith'

6. Не используйте «магические числа»

Магические числа — это странные числа, которые появляются в коде и не имеют четкого значения. Давайте рассмотрим пример:

import random

# This is bad
def roll():
    return random.randint(0, 36)  # what is 36 supposed to represent?

# This is good
ROULETTE_POCKET_COUNT = 36

def roll():
    return random.randint(0, ROULETTE_POCKET_COUNT)

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

7. Используйте доменные имена решений

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

# This is good
score_list = [12, 33, 14, 24]
word_dict = {
    'a': 'apple',
    'b': 'banana',
    'c': 'cherry',
}

А вот плохой пример (потому что по имени переменной нельзя определить тип данных):

# This is bad
names = ["Nick", "Mike", "John"]

8. Не добавляйте лишний контекст

Не добавляйте ненужные данные к именам переменных, особенно если вы работаете с классами.

# This is bad
class Person:
    def __init__(self, person_first_name, person_last_name, person_age):
        self.person_first_name = person_first_name
        self.person_last_name = person_last_name
        self.person_age = person_age


# This is good
class Person:
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age

Мы уже внутри Personкласса, поэтому нет необходимости добавлять person_префикс к каждой переменной класса.

Функции

1. Используйте глаголы для имен функций

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

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

# This is bad
def get_name(): pass
def fetch_age(): pass

# This is good
def get_name(): pass
def get_age(): pass

3. Пишите короткие и простые функции

4. Функции должны выполнять только одну задачу

Если ваша функция содержит ключевое слово «и», вы, вероятно, можете разделить ее на две функции. Давайте посмотрим на пример:

# This is bad
def fetch_and_display_personnel():
    data = # ...

    for person in data:
        print(person)


# This is good
def fetch_personnel():
    return # ...

def display_personnel(data):
    for person in data:
        print(person)

Функции должны делать одну вещь, и, как читатель, они делают то, что вы от них ожидаете.

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

5. Сведите аргументы к минимуму

Аргументы в вашей функции должны быть сведены к минимуму. В идеале ваши функции должны иметь только один-два аргумента. Если вам нужно предоставить больше аргументов функции, вы можете создать объект конфигурации, который вы передаете функции, или разделить его на несколько функций.

Пример:

# This is bad
def render_blog_post(title, author, created_timestamp, updated_timestamp, content):
    # ...

render_blog_post("Clean code", "Nik Tomazic", 1622148362, 1622148362, "...")


# This is good
class BlogPost:
    def __init__(self, title, author, created_timestamp, updated_timestamp, content):
        self.title = title
        self.author = author
        self.created_timestamp = created_timestamp
        self.updated_timestamp = updated_timestamp
        self.content = content

blog_post1 = BlogPost("Clean code", "Nik Tomazic", 1622148362, 1622148362, "...")

def render_blog_post(blog_post):
    # ...

render_blog_post(blog_post1)

6. Не используйте флаги в функциях

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

text = "This is a cool blog post."


# This is bad
def transform(text, uppercase):
    if uppercase:
        return text.upper()
    else:
        return text.lower()

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


# This is good
def uppercase(text):
    return text.upper()

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

uppercase_text = uppercase(text)
lowercase_text = lowercase(text)

7. Избегайте побочных эффектов

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

Комментарии

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

В чем разница между комментариями кода и документацией?

Тип

Ответы

Акционер

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

Когда и как

Пользователи

Комментарии к коду

Почему

Разработчики

Чистый код

какая

Разработчики

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

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

Комментирование плохого кода — т . е # TODO: RE-WRITE THIS TO BE BETTER. — поможет вам только в краткосрочной перспективе. Рано или поздно одному из ваших коллег придется поработать с вашим кодом, и он в конечном итоге перепишет его, потратив несколько часов, пытаясь понять, что он делает.

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. Не добавляйте шумовые комментарии

Не добавляйте комментарии, которые не добавляют ничего ценного в код. Это плохо:

numbers = [1, 2, 3, 4, 5]

# This variable stores the average of list of numbers.
average = sum(numbers) / len(numbers)
print(average)

Это тоже плохо:

4. Используйте правильные типы комментариев

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

def model_to_dict(instance, fields=None, exclude=None):
    """
    Returns a dict containing the data in ``instance`` suitable for passing as
    a Form's ``initial`` keyword argument.
    ``fields`` is an optional list of field names. If provided, return only the
    named.
    ``exclude`` is an optional list of field names. If provided, exclude the
    named from the returned dict, even if they are listed in the ``fields``
    argument.
    """
    opts = instance._meta
    data = {}
    for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
        if not getattr(f, 'editable', False):
            continue
        if fields is not None and f.name not in fields:
            continue
        if exclude and f.name in exclude:
            continue
        data[f.name] = f.value_from_object(instance)
    return data

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

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

Декораторы, менеджеры контекста, итераторы и генераторы

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

Декораторы

Декораторы — чрезвычайно мощный инструмент в Python, который позволяет нам добавлять к функции некоторые пользовательские функции. По своей сути, это просто функции, вызываемые внутри функций. Используя их, мы используем принцип SoC (разделение задач) и делаем наш код более модульным. Изучите их, и вы будете на пути к коду Pythonic!

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

def ask_for_passcode(func):
    def inner():
        print('What is the passcode?')
        passcode = input()

        if passcode != '1234':
            print('Wrong passcode.')
        else:
            print('Access granted.')
            func()

    return inner


@ask_for_passcode
def start():
    print("Server has been started.")


@ask_for_passcode
def end():
    print("Server has been stopped.")


start()  # decorator will ask for password
end()  # decorator will ask for password

Наш сервер теперь будет запрашивать пароль каждый раз start()или при end()вызове.

Менеджеры контекста

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

Давайте посмотрим на пример:

with open('wisdom.txt', 'w') as opened_file:
    opened_file.write('Python is cool.')

# opened_file has been closed.

Без менеджера контекста наш код выглядел бы так:

file = open('wisdom.txt', 'w')
try:
    file.write('Python is cool.')
finally:
    file.close()

Итераторы

Итератор — это объект, который содержит счетное количество значений. Итераторы позволяют повторять объект, что означает, что вы можете пройти через все значения.

Допустим, у нас есть список имен, и мы хотим просмотреть его. Мы можем пройти через него, используя next(names):

names = ["Mike", "John", "Steve"]
names_iterator = iter(names)

for i in range(len(names)):
    print(next(names_iterator))

Или используйте расширенный цикл:

names = ["Mike", "John", "Steve"]

for name in names:
    print(name)
Внутри расширенных циклов избегайте использования имен переменных, таких как itemили value, потому что это затрудняет определение того, что хранится в переменной, особенно во вложенных расширенных циклах.

Генераторы

Генератор — это функция в Python, которая возвращает объект итератора вместо одного значения. Основное различие между обычными функциями и генераторами заключается в том, что генераторы используют yieldключевое слово вместо return. Каждое следующее значение в итераторе извлекается с помощью next(generator).

Допустим, мы хотим сгенерировать первые nкратные x. Наш генератор будет выглядеть примерно так:

def multiple_generator(x, n):
    for i in range(1, n + 1):
        yield x * i

multiples_of_5 = multiple_generator(5, 3)
print(next(multiples_of_5))  # 5
print(next(multiples_of_5))  # 10
print(next(multiples_of_5))  # 15

Модульность и классы

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

Разделение кода на несколько классов упростит его понимание и поддержку. Не существует фиксированного правила относительно длины файла или класса, но старайтесь, чтобы они были небольшими (желательно до 200 строк).

Структура проекта Django по умолчанию — хороший пример того, как должен быть структурирован ваш код:

awesomeproject/
├── main/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── blog/
│   ├── migrations/
│   │   └── __init__.py
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── templates

Django — это фреймворк MTV (модель — шаблон — представление), который похож на фреймворк MVC , который мы обсуждали ранее. Этот шаблон делит логику программы на три взаимосвязанные части. Вы можете видеть, что каждое приложение находится в отдельном каталоге, и каждый файл служит одной конкретной цели. Если ваш проект разбит на несколько приложений, вы должны убедиться, что приложения не слишком сильно зависят друг от друга.

Тестирование

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

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

  1. Тестирование на Питоне
  2. Современная разработка через тестирование на Python

Вывод

Писать чистый код сложно. Не существует единого рецепта, которому вы могли бы следовать, чтобы писать хороший, чистый код. Для освоения требуется время и опыт. Мы рассмотрели некоторые стандарты кодирования и общие рекомендации, которые помогут вам писать более качественный код. Один из лучших советов, который я могу вам дать, — оставаться последовательным и пытаться писать простой код, который легко тестировать. Если вы обнаружите, что ваш код трудно тестировать, вероятно, его трудно использовать.

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

ист.: https://testdriven.io/blog/clean-code-python/