Fields and default values¶
The first model required us to pass active every time. Let us specify the value to use when the argument is omitted:
from bytespec import ProtoModel, field
class User(ProtoModel):
name: str
active: bool = field(default=True)
user = User(name="Anna")
decoded = User.decode(user.encode())
print(decoded.active) # True
field() configures an individual field. Here, default=True lets you omit active from the constructor. The field remains part of the message and is written even when the default value is used.
Explicit values take precedence over defaults¶
user = User(name="Anna", active=False)
print(User.decode(user.encode()).active) # False
The default applies only when the argument is omitted. name is still required. An unknown argument or a missing required field raises TypeError.
A serialized field specifically requires field(default=...). A plain assignment such as active: bool = True leaves the class attribute outside the message; it is not shorthand for a default value.
Field order¶
Without additional settings, fields are written in declaration order. Do not rearrange them if the other side already reads this format.
For an explicit order, the first argument to field() specifies the index:
class User(ProtoModel):
active: bool = field(1, default=True)
name: str = field(0)
user = User(name="Anna")
print(User.decode(user.encode()).name) # Anna
Despite its position in the class, name with index 0 is written first. Indices must start at zero, with no duplicates or gaps. You can omit them for an ordinary model; the rules for mixing explicit and automatic indices are covered in Field declaration rules.
When you need other settings¶
field() also lets you make a field optional and change its binary representation. We will introduce these settings as needed. The exact signature is available at bytespec.field().
Next: Numbers and other types, where we will add a numeric user identifier.