[plum.structure] Tutorial: Comparing Structures

Structure types support equality comparisons in a similar manner as list, but with a couple differences. This tutorial explains those differences and demonstrates special features of structure types that relate to comparisons.

Basic Rules

Structure instances support comparisons against other lists. Similar to list comparisons, the list being compared against must contain the same number of items, and for each item, the item values must be equal:

>>> from plum import unpack
>>> from plum.structure import Member, Structure
>>> from plum.int.little import UInt8
>>>
>>> class Struct1(Structure):
...     m1: int = Member(cls=UInt8)
...     m2: int = Member(cls=UInt8)
...
>>> struct1 = unpack(Struct1, b'\x01\x02')
>>> struct1
Struct1(m1=1, m2=2)
>>>
>>> struct1 == [1]  # missing m2 member
False
>>> struct1 == [1, 2, 3]  # extra m3 member
False
>>> struct1 == [2, 1]  # members out of order
False
>>> struct1 == [1, 2]  # exact match
True

Tip

Create an instance of the same structure type using keyword arguments to improve code readability and avoid worrying about specifying the members in the wrong order:

>>> struct1 == Struct1(m1=1, m2=2)
True

Members With Default

For structure types containing members with default values, the basic rules remain the same. Each member value must be specified and equal. But, if the expectation for a member is the same as the default, instantiating the structure type with the expected values allows the defaulted member to be left unspecified:

>>> from plum import unpack
>>> from plum.structure import Member, Structure
>>> from plum.int.little import UInt8
>>>
>>> class Struct2(Structure):
...     m1: int = Member(cls=UInt8)
...     m2: int = Member(cls=UInt8, default=2)
...
>>> struct2 = unpack(Struct2, b'\x01\x02')
>>> struct2
Struct2(m1=1, m2=2)
>>>
>>> struct2 == [1]  # missing m2 member
False
>>> struct2 == Struct2(m1=1)  # compares with m2 default
True

Ignored Members

For structure types containing members defined as ignored, the basic rules remain the same. But special rules apply when compared against an instance of the same type. In that special case, mismatched values of members defined as ignored do not cause the comparison to fail:

>>> from plum import unpack
>>> from plum.structure import Member, Structure
>>> from plum.int.little import UInt8
>>>
>>> class Struct3(Structure):
...     m1: int = Member(cls=UInt8)
...     m2: int = Member(cls=UInt8, default=2)
...     m3: int = Member(cls=UInt8, default=0, ignore=True)
...
>>> struct3 = unpack(Struct3, b'\x01\x02\x03')
>>> struct3
Struct3(m1=1, m2=2, m3=3)
>>>
>>> struct3 == Struct3(m1=1, m2=2, m3=99)  # m3 does not match (but ignored)
True
>>> struct3 == Struct3(m1=1, m2=2)  # m3 expectation defaulted (but ignored)
True