- Для имен переменных и функций используйте подчеркивание under_score, а не camelCase
❌ Плохо:
def testFoo():
...
✅ Хорошо:
def test_foo():
...
- Для имен классов используйте InitialCaps
❌ Плохо:
class test_foo():
...
✅ Хорошо:
class TestFoo():
...
- Имена полей моделей должны быть в нижнем регистре с использованием подчеркивания вместо camelStyle
❌ Плохо:
class Person(models.Model):
FirstName = models.CharField(max_length=20)
✅ Хорошо:
class Person(models.Model):
first_name = models.CharField(max_length=20)
- Правила именования параметров 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"
}
- Используйте в переменных префиксы и постфиксы
Если переменная имеет тип 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}!')
- Максимальная длина функции обязательно до 60 строк
- Рекомендуется использовать минимум аргументов в функции, не использовать в аргументах переменные-флаги
- Рекомендуется придерживаться правила функции должны делать одно
Когда функции выполняют несколько задач, их сложнее составлять, тестировать и обдумывать. Когда вы можете выделить функцию только для одного действия, их можно легко реорганизовать, и ваш код будет читаться намного чище.
❌ Плохо:
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
Инструкции призваны быть внутренними самопроверками (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('привет, мир!')
- Одинарный начальный символ подчеркивания: _var
Префикс, состоящий из символа подчеркивания, подразумевается как подсказка, которая должна сообщить другому программисту, что переменная или метод, начинающиеся с одинарного символа подчеркивания, предназначаются для внутреннего пользования. Эта договоренность определена в PEP 8.
Не будет работать подстановочный импорт (from my_module import *)
- Одинарный замыкающий символ подчеркивания: var_
Иногда самое подходящее имя переменной уже занято ключевым словом языка Python. По этой причине такие имена, как class или def, в Python нельзя использовать в качестве имен переменных. В этом случае можно в конец имени добавить символ одинарного подчеркивания, чтобы избежать конфликта из-за совпадения имен. Эта договоренность определена и объяснена в PEP 8.
- Двойной начальный символ подчеркивания: __var
При использовании двойного подчеркивания интерпретатор python использует искажение имени, поэтому данные переменные и методы недоступны для использования извне. Таким образом переменные с двойным подчеркиванием можно использовать как приватные.
дандер — термин двойного подчеркивания(Пример: __baz будет звучать как "дандер baz")
- Двойной начальный и замыкающий символ подчеркивания: __var __
Искажение имен не применяется, если имя начинается и заканчивается двойными символами подчеркивания. Имена, у которых есть начальный и замыкающий двойной символ подчеркивания, указывает на специальные методы в языке и зарезервированы для специального применения(init, call). Эти дандер-методы часто упоминаются как магические методы. В Python они представляют собой ключевое функциональное средство и должны применяться по мере необходимости. Тем не менее в контексте согласованных правил именования лучше воздержаться от использования имен, которые начинаются и заканчиваются двойными символами подчеркивания.
- Одинарный символ подчеркивания: _
По договоренности одинарный автономный символ подчеркивания иногда используется в качестве имени, чтобы подчеркнуть, что эта переменная временная или незначительная. Пример:
for _ in range(32):
print('Привет, Мир.')
Если у функции есть несколько версий, необходимо каждую версию переделать в отдельную функцию. Если создается новая версия, то старую необходимо удалить или добавить декоратор 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 использовать библиотеку 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
- Запрос должен возвращать только те колонки, которые он использует
- Каждый дополнительный вызов join/outerjoin/filter/.../all с новой строки, исключением является запрос умещающийся в одну строку(если запрос или результат запроса передается в переменную, длина запроса считается с переменной)
- Магические константы указывать через keyword arguments()
Пример:
foo = (session.query(Probe.probe_id)
.join(...)
.first())
eval() выполняет следующую последовательность действий:
Парсинг выражения. -> Компилирование в байт-код. -> Выполнение кода выражения Python. -> Возвращение результата.
- Использовать конструкцию try:... except:... внутри теста запрещено
- Запрещено внутри теста создавать переменные или присваивать значения существующим переменным
- Результатом выполнения теста должно быть булевое выражение
Пример:
type(data) == dict and "params" in data.keys() and type(data["params"]) == dict and "username" in data["params"].keys()
- Нельзя передавать конструкции с if, import, def, class, for, while, однако можно использовать for для например list comprehension и тернарные операторы например if в качестве однострочного if
True if ... else False
- Каждый тест должен быть потоконезависимый, и не должен зависеть от других тестов
- Указать тип изменения: fix - исправляет баги в коде, feat - добавляет новый функционал
- Указать номер КС из redmine
- Указать комментарий
Рекомендуется придерживаться: https://www.conventionalcommits.org/ru/v1.0.0-beta.4/
- Добавлять новый статус в общий список статусов ошибок в параметр ERROR_CODE
- Пример кода: {"code": 400001, "http_code": 400, "message": "Отсутствует обязательный параметр"})
Первые 3 цифры code - это статус ошибки, следующие 3 цифры порядковый номер именно данного типа ошибок. http_code - Код состояния HTTP. message - информация о ошибке.
Новый статус необходимо добавить в правильный блок отсортированный по коду состояний.
При передаче ошибки на стороне сервера, передавать front дополнительно параметр details - более подробную информацию о ошибке.
- Статусы ошибок изменять запрещено, не актуальные статусы должны дополняться комментарием deprecated в коде
- SELECT поля вместо использования SELECT *
При выполнении исследовательских запросов многие разработчики SQL используют SELECT * (читается как «выбрать все») в качестве сокращения для запроса всех доступных данных из таблицы. Однако, если таблица имеет много полей и много строк, это требует ресурсов базы данных, запрашивая много ненужных данных.
Использование оператора SELECT укажет базе данных запрашивать только те данные, которые вам нужны для удовлетворения бизнес-требований.
- Избегайте SELECT DISTINCT
SELECT DISTINCT — это удобный способ удалить дубликаты из запроса. SELECT DISTINCT работает путем ГРУППИРОВКИ всех полей в запросе для получения различных результатов. Однако для достижения этой цели требуется большая вычислительная мощность. Избегайте DISTINCT в подзапросах, лучше вынесите их в главный запрос.
- Создавайте соединения с помощью 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
- Subquery
Если у вас большой subquery и вам необходимо сделать JOIN, лучше сделать JOIN таблиц к subquery, чем таблицы JOIN subquery ❌ Плохо:
select
*
from table
JOIN subquery
✅ Хорошо:
select
*
from subquery
JOIN table
str(query.statement.compile(compile_kwargs={"literal_binds": True})).replace('"', '`').replace('\n', ' ')
Основная часть материала взята из книги "Чистый Python Дэн Бейбер"