3.2.1.2. Struct#

A struct describes a finite collection of named fields. In contrast to a sequence, a struct utilizes Python classes as its model. The annotation feature in Python enables the definition of custom types as annotations, enabling this special struct class to create a model solely based on class annotations. Additionally, it generates a dataclass of the provided model, offering a standardized string representation.

Several differences exist between a Sequence and a Struct, with the most significant ones highlighted below:

Behaviour of structs and sequences#

Sequence

Struct

Model Type

dict

type

Inheritance

No

Yes

Attribute Access

x["name"]

getattr(x, "name", None)

Unpacked Type (also needed to pack)

dict [*]

instance of model

Documentation

No

Yes

As evident from the comparison, the Struct class introduces new features such as inheritance and documentation support. It’s crucial to note that inheritance uses struct types exclusively.

The Sequence class implements a specific process for creating an internal representation of the given model. The Struct class enhances this process by handling default values, replacing types for documentation purposes, or removing annotation fields directly from the model. Additionally, this class adds __struct__ to the model afterward.

Implementation Note

If you decide to use the annotation feature from the __future__ module, it is necessary to enable S_EVAL_ANNOTATIONS since it “Stringizes” all annotations. inspect then evaluates all strings, introducing a potential security risk. Exercise with caution when evaluating code!

Specifying structs is as simple as defining Python Classes:

>>> @struct
... class BaseFormat:
...     magic: b"MAGIC"
...     a: uint8
...

Internally, a representation with all required fields and their corresponding names is created. As b"MAGIC" or uint8 are instances of types, the type replacement for documentation purposes should be enabled, as shown in Special Methods for Struct-Like objects.

As described above, this class introduces an easy-to-use inheritance system using the method resolution order of Python:

>>> @struct
... class Format(BaseFormat):
...     b: uint32
...     c: uint16
...
>>> list(Format.__struct__.get_members())
['magic', 'a', 'b', 'c']

Programmers Note

As the Struct class is a direct subclass of Sequence, nesting is supported by default. That means, so-called anonymous inner structs can be defined within a class definition.

>>> @struct
... class Format:
...     a: uint32
...     b: {"c": uint8}
...

It is not recommended to use this technique as the inner structs can’t be used anywhere else. Anonymous inner union definitions are tricky and are not officially supported yet. There are workarounds to that problem, which are discussed in the API documentation of Sequence.