Skip to content

BigForLy/bskb-python-clean-code

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 

Repository files navigation

bskb-python-clean-code

Правила именования функций, переменных, ...

  1. Для имен переменных и функций используйте подчеркивание under_score, а не camelCase

❌ Плохо:

def testFoo():
    ...

✅ Хорошо:

def test_foo():
    ...
  1. Для имен классов используйте InitialCaps

❌ Плохо:

class test_foo():
    ...

✅ Хорошо:

class TestFoo():
    ...
  1. Имена полей моделей должны быть в нижнем регистре с использованием подчеркивания вместо camelStyle

❌ Плохо:

class Person(models.Model):
    FirstName = models.CharField(max_length=20)

✅ Хорошо:

class Person(models.Model):
    first_name = models.CharField(max_length=20)
  1. Правила именования параметров JSON

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

❌ Плохо:

{
    "method": "a",
    "params": {
      "filter_date-time": "true"
    }
}

✅ Хорошо:

{
    "method": "a",
    "params": {
      "filterDateTime": "true"
    }
}

В имени идет сначала имя сущности, потом её свойство

❌ Плохо:

{
    "uin_jounral": "aa",
    "id_journal": "1"
}

✅ Хорошо:

{
    "jounral_uin": "aa",
    "journal_id": "1"
}
  1. Используйте в переменных префиксы и постфиксы

Если переменная имеет тип datetime, она должна заканчиваться на _at. Если переменная имеет логический тип, она должна начинаться с is_, are_, was_, has_ и так далее.

❌ Плохо:

updated = ...

✅ Хорошо:

updated_at = ...
is_updated = ...

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

❌ Плохо:

c = 0  # Такой пример не несет понятной логики, трудночитаем в проекте

✅ Хорошо:

columnCount = 0

Избегайте вводящих в заблуждение имен

❌ Плохо:

day = list(day)

✅ Хорошо:

listOfDay = list(day)

Избегайте магических констант

❌ Плохо:

if attribute_type == "system":
   ...

✅ Хорошо:

SYSTEM_ATTRIBUTE_TYPE = "system"
if attribute_type == SYSTEM_ATTRIBUTE_TYPE:
   ...

Избегайте отрицательных условных выражений

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

❌ Плохо:

def notSystemTypeAttribute(attributeType):
   if attributeType != "system":
      True
   else:
      False

if notSystemTypeAttribute(attributeType) != False:  # Двойное отрицание: не системный и не равно
   ...
else:
   ...

✅ Хорошо:

def SystemTypeAttribute(attributeType):
   if attributeType == "system":
      True
   else:
      False

if SystemTypeAttribute(attributeType) == False:
   ...
else:
   ...

Форматирование строковых значений

Для форматирования строк рекомендуется использовать форматирование строковыми литералами

Пример:

name = "Me"
print(f'Привет, {name}!')

Правила написания функций

  1. Максимальная длина функции обязательно до 60 строк
  2. Рекомендуется использовать минимум аргументов в функции, не использовать в аргументах переменные-флаги
  3. Рекомендуется придерживаться правила функции должны делать одно

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

Не добавляйте ненужный контекст

❌ Плохо:

class Attribute:
   self.attribute_type = None
   self.attribute_name = None
   self.attribute_value = None

✅ Хорошо:

class Attribute:
   self.type = None
   self.name = None
   self.value = None

Инструкция assert

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

инструкция_assert ::= "assert" выражение1 ["," выражение2]
где выражение1 - условие, выражение2 - комментарий к ошибке

Пример:

assert 0 <= price <= 100, "Error"

Ошибка:

AssertionError: Error

Предостережение:

Не использовать assert для валидации данных!

Разворачивание констант списка, словаря или множества

❌ Плохо:

names = ['Элис', 'Боб', 'Дилберт']

✅ Хорошо:

 names = [
... 'Элис',
... 'Боб',
... 'Дилберт'
... ]

Работа с файлами

❌ Плохо:

f = open('hello.txt', 'w')
f.write('привет, мир!')
f.close()

Вредно:

f = open('hello.txt', 'w')
try:
  f.write('привет, мир!')
finally:
  f.close()

✅ Хорошо:

with open('hello.txt', 'w') as f:
  f.write('привет, мир!')

Одинарные и двойные подчеркивания, дандеры

  1. Одинарный начальный символ подчеркивания: _var

Префикс, состоящий из символа подчеркивания, подразумевается как подсказка, которая должна сообщить другому программисту, что переменная или метод, начинающиеся с одинарного символа подчеркивания, предназначаются для внутреннего пользования. Эта договоренность определена в PEP 8.

Не будет работать подстановочный импорт (from my_module import *)

  1. Одинарный замыкающий символ подчеркивания: var_

Иногда самое подходящее имя переменной уже занято ключевым словом языка Python. По этой причине такие имена, как class или def, в Python нельзя использовать в качестве имен переменных. В этом случае можно в конец имени добавить символ одинарного подчеркивания, чтобы избежать конфликта из-за совпадения имен. Эта договоренность определена и объяснена в PEP 8.

  1. Двойной начальный символ подчеркивания: __var

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

дандер — термин двойного подчеркивания(Пример: __baz будет звучать как "дандер baz")

  1. Двойной начальный и замыкающий символ подчеркивания: __var __

Искажение имен не применяется, если имя начинается и заканчивается двойными символами подчеркивания. Имена, у которых есть начальный и замыкающий двойной символ подчеркивания, указывает на специальные методы в языке и зарезервированы для специального применения(init, call). Эти дандер-методы часто упоминаются как магические методы. В Python они представляют собой ключевое функциональное средство и должны применяться по мере необходимости. Тем не менее в контексте согласованных правил именования лучше воздержаться от использования имен, которые начинаются и заканчиваются двойными символами подчеркивания.

  1. Одинарный символ подчеркивания: _

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

for _ in range(32):
 print('Привет, Мир.')

Использование декоратора deprecated

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

Использование кода:

from deprecated import deprecated
@deprecated(reason="use another function")
def foo():
    print('deprecated func')

Консоль:

DeprecationWarning: Call to deprecated function (or staticmethod) foo. (use another function)
  foo()
deprecated func

https://github.com/tantale/deprecated

Версионность методов

Если у метода есть несколько версий, то необходимо каждую версию выносить в отдельную функцию.

Методы для каждой версии необходимо именовать по правилам: Имя головной функции + _ v + номер версии

Пример:

def get_probes_card_by_journal(self, params):
    if self.version == 1:
        self.get_probes_card_by_journal_v1(params)
    elif self.version == 2:
        self.get_probes_card_by_journal_v2(params)

Стиль моделей

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

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

  • Все поля в базе
  • Настраиваемые атрибуты менеджера
  • class Meta
  • def __str __()
  • def save()
  • def get_absolute_url()

Singleton

Singleton - в однопоточном приложении будет единственный экземпляр некоторого класса.

Для Singleton использовать библиотеку pip install singleton-decorator

Пример:

@singleton
class YourClass:
    def method(self):
        ...

Документация: https://pypi.org/project/singleton-decorator/

Создание миграций

Не изменять миграции которые попали в master/develop

Правила написания запросов к базе данных

Использовать select_related, когда объект, который вы собираетесь выбрать, является одним объектом, что означает пересылку ForeignKey, OneToOne и обратный OneToOne.

select_related работает путем создания соединения SQL и включения полей связанного объекта в оператор SELECT. По этой причине select_related получает связанные объекты в том же запросе к базе данных.

База данных для примера:

class Publisher(models.Model):
    name = models.CharField(max_length=300)

    def __str__(self):
        return self.name

class Book(models.Model):
    name = models.CharField(max_length=300)
    price = models.IntegerField(default=0)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)

    class Meta:
        default_related_name = 'books'

    def __str__(self):
        return self.name
        
class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)

    class Meta:
        default_related_name = 'stores'

    def __str__(self):
        return self.name

❌ Плохо:

queryset = Book.objects.all()
books = []
for book in queryset:
    books.append({'id': book.id, 'name': book.name, 'publisher': book.publisher.name})
# Number of Queries : 101
# В Book 100 элементов. При каждом обращении делает новый запрос к базе

✅ Хорошо:

queryset = Book.objects.select_related('publisher').all()
books = []
for book in queryset:
    books.append({'id': book.id, 'name': book.name, 'publisher': book.publisher.name})
# Number of Queries : 1
# Не делает новый запрос к базе при обращение

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

prefetch_related выполняет отдельный поиск для каждой связи и выполняет «объединение» в Python. Он отличается от select_related. prefetch_related выполнял JOIN с использованием Python, а не в базе данных.

❌ Плохо:

queryset = Store.objects.all()
stores = []
for store in queryset:
    stores.append({'id': store.id, 'name': store.name, 'books': books})
# Number of Queries : 10
# В Store 10 элементов. Здесь происходит один запрос для выборки всех хранилищ, и во время итерации по каждому хранилищу выполняется другой запрос

✅ Хорошо:

queryset = Store.objects.prefetch_related('books')
stores = []
for store in queryset:
    stores.append({'id': store.id, 'name': store.name, 'books': books})
# Number of Queries : 1

Правила форматирования запросов SQLAlchemy

  1. Запрос должен возвращать только те колонки, которые он использует
  2. Каждый дополнительный вызов join/outerjoin/filter/.../all с новой строки, исключением является запрос умещающийся в одну строку(если запрос или результат запроса передается в переменную, длина запроса считается с переменной)
  3. Магические константы указывать через keyword arguments()

Пример:

foo = (session.query(Probe.probe_id)
       .join(...)
       .first())

Правила для написания тестов eval()

eval() выполняет следующую последовательность действий:

Парсинг выражения. -> Компилирование в байт-код. -> Выполнение кода выражения Python. -> Возвращение результата.

  1. Использовать конструкцию try:... except:... внутри теста запрещено
  2. Запрещено внутри теста создавать переменные или присваивать значения существующим переменным
  3. Результатом выполнения теста должно быть булевое выражение

Пример:

type(data) == dict and "params" in data.keys() and type(data["params"]) == dict and "username" in data["params"].keys()
  1. Нельзя передавать конструкции с if, import, def, class, for, while, однако можно использовать for для например list comprehension и тернарные операторы например if в качестве однострочного if
True if ... else False
  1. Каждый тест должен быть потоконезависимый, и не должен зависеть от других тестов

Правила формирования commit

  1. Указать тип изменения: fix - исправляет баги в коде, feat - добавляет новый функционал
  2. Указать номер КС из redmine
  3. Указать комментарий

Рекомендуется придерживаться: https://www.conventionalcommits.org/ru/v1.0.0-beta.4/

Правила оформления статусов ошибок

  1. Добавлять новый статус в общий список статусов ошибок в параметр ERROR_CODE
  2. Пример кода: {"code": 400001, "http_code": 400, "message": "Отсутствует обязательный параметр"})

Первые 3 цифры code - это статус ошибки, следующие 3 цифры порядковый номер именно данного типа ошибок. http_code - Код состояния HTTP. message - информация о ошибке.

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

При передаче ошибки на стороне сервера, передавать front дополнительно параметр details - более подробную информацию о ошибке.

  1. Статусы ошибок изменять запрещено, не актуальные статусы должны дополняться комментарием deprecated в коде

Оптимизация MySQL запросов

  1. SELECT поля вместо использования SELECT *

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

Использование оператора SELECT укажет базе данных запрашивать только те данные, которые вам нужны для удовлетворения бизнес-требований.

  1. Избегайте SELECT DISTINCT

SELECT DISTINCT — это удобный способ удалить дубликаты из запроса. SELECT DISTINCT работает путем ГРУППИРОВКИ всех полей в запросе для получения различных результатов. Однако для достижения этой цели требуется большая вычислительная мощность. Избегайте DISTINCT в подзапросах, лучше вынесите их в главный запрос.

  1. Создавайте соединения с помощью INNER JOIN (а не WHERE)

❌ Плохо:

SELECT
  Customers.CustomerID,
  Customers.name,
  Sales.LastSaleDate
FROM Customers,
     Sales
WHERE Customers.CustomerID = Sales.CustomerID

✅ Хорошо:

SELECT
  Customers.CustomerID,
  Customers.name,
  Sales.LastSaleDate
FROM Customers
  INNER JOIN Sales
    ON Customers.CustomerID = Sales.CustomerID
  1. Subquery

Если у вас большой subquery и вам необходимо сделать JOIN, лучше сделать JOIN таблиц к subquery, чем таблицы JOIN subquery ❌ Плохо:

select 
    * 
from table
    JOIN subquery

✅ Хорошо:

select 
    * 
from subquery
    JOIN table

Получить query запрос с параметрами

str(query.statement.compile(compile_kwargs={"literal_binds": True})).replace('"', '`').replace('\n', ' ')

Основная часть материала взята из книги "Чистый Python Дэн Бейбер"

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published