TGStat
TGStat
Type to search
Advanced channel search
  • flag English
    Site language
    flag Russian flag English flag Uzbek
  • Sign In
  • Catalog
    Channels and groups catalog Regional compilations Thematic compilations Платные каналы Search for channels
    Add a channel/group
  • Ratings
    Rating of channels Rating of groups Posts rating
    Ratings of brands and people
  • Analytics
  • Search by posts
  • Telegram monitoring
  • Promotion
    Advertising through Yandex Business Advertising in channels through TGStat Agency Advertising on TGStat.ru website
Советы разработчикам (python и не только)

15 Oct 2023, 14:59

Open in Telegram Share Report

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

Иногда, по каким-то причинам мы не можем выполнить всю инициализацию при создании класса (в конструкторе или в __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
Catalog
Channels and groups catalog Channels compilations Search for channels Add a channel/group
Ratings
Rating of Telegram channels Rating of Telegram groups Posts rating Ratings of brands and people
API
API statistics Search API of posts API Callback
Our channels
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Read
Академия TGStat Telegram Research 2019 Telegram Research 2021 Telegram Research 2023
Contacts
Справочный центр Support Email Jobs
Miscellaneous
Terms and conditions Privacy policy Public offer
Our bots
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot