TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
Советы разработчикам (python и не только)

15 Oct 2023, 14:59

Открыть в Telegram Поделиться Пожаловаться

Двухфазная инициализация

Иногда, по каким-то причинам мы не можем выполнить всю инициализацию при создании класса (в конструкторе или в __init__). Например, это может быть выполнение асинхронного ввода/вывода, простановка циклических ссылок между двумя созданными объектами или особоый механизм обработки ошибок инициализации. В этом случае иногда создают вспомогательный метод, который нужно вызвать сразу после создания объекта. Стоит использовать такой подход с осторожностью.

Например, мы хотим создать гейтвей для работы с БД. Следующий код не будет работать:

class SomeGW:
def __init__(self, db_uri):
self.connection = await asyncpg.connect(db_uri)

gw = SomeGw("postgresql://postgres@localhost/test")

Мы не можем выполнять async код в ините класса, поэтому можно попытаться сделать двухфазную инициализацию:

class SomeGW:
def __init__(self):
self.connection = None

async def connect(self, db_uri):
self.connection = await asyncpg.connect(db_uri)

gw = SomeGw()
await gw.connect("postgresql://postgres@localhost/test")

В таком случае необходимо следить, что соединение не будет использовано до завершения второй фазы инициализации (вызова connect). Также, у созданного объекта формально self.connection может быть None, что приведет к дополнительным проверкам в коде всех методов и предупреждениям линтера. Проще было ввести дополнительную функцию:

class SomeGW:
def __init__(self, connection):
self.connection = connection

async def new_some_gw(db_uri):
connection = await asyncpg.connect(db_uri)
return SomeGw(connection)

gw = await new_some_gw("postgresql://postgres@localhost/test")

Проблемы многофазной инициализации:
• Объект может быть инициализирован частично, что приведет к ошибкам выполнения
• Линтеры будут требовать дополнительных проверок в методах
• Корректная последовательность инициализации класса неочевидна из его API. Ситуация становится сложнее, если у нас есть несколько вариантов второй фазы
• Возможно нарушение принципа единственности ответственности: объект смешивает логику, ради которой он создавался, и сложную процедуру инициализации

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

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

Дополнительные материалы
• http://neo.dmcs.p.lodz.pl/symos/wyklady/04-TwoPhase.pdf
• https://wiki.wxpython.org/TwoStageCreation
• https://peps.python.org/pep-0489/

13.8k 4 47 89 89
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot