Собираем сообщение

Теперь соединим уже знакомые возможности: модель автора, список тегов и необязательную заметку. Этот пример можно выполнить целиком.

from bytespec import ProtoModel, field
from bytespec.types import UInt32

class User(ProtoModel):
    id: UInt32
    name: str

class Message(ProtoModel):
    author: User
    text: str
    tags: list[str] = field(default_factory=list)
    note: str | None = field(flag=0)

message = Message(
    author=User(id=42, name="Anna"),
    text="Hello!",
    tags=["news"],
)
encoded = message.encode()
decoded = Message.decode(encoded)

print(decoded.author.name)  # Anna
print(decoded.text)         # Hello!
print(decoded.tags)         # ['news']
print(decoded.note)         # None

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

Изменим заметку и запишем сообщение ещё раз:

decoded.note = "Updated"
updated = Message.decode(decoded.encode())
print(updated.note)  # Updated

Полезно попробовать этот пример с пустым tags и с note="": оба значения будут сохранены. Только note=None убирает optional-поле из сообщения.

Теперь можно попробовать модель на одном пакете своего протокола. Header и framing показывает, как настроить служебные байты; Проверка значений модели — как проверить связи между полями; Обработка ошибок — как обработать ошибки. Если нужно читать несколько сообщений из одного буфера, откройте Файлы и несколько сообщений.