Batu Lab NotesPractical developer guides

Represent a compact measurement row with namedtuple

By Batu ยท English technical notes

Also published in our Blogger archive.

A measurement often travels as a short, fixed-shape value: a numeric reading plus when it was taken. collections.namedtuple() makes that shape explicit without giving up tuple unpacking or indexing. Here, Measurement has sensor_celsius and taken_at; taken_at defaults to the rightmost value, 'unrecorded'.

The example creates Measurement(21.5). The resulting row supports row.sensor_celsius, row[0], tuple(row), and _asdict(). Calling _replace(sensor_celsius=22.0) returns a new row, leaving the original 21.5 reading intact. The assertions check those particular transformations before the output is printed.

A named tuple is immutable, so direct assignment such as row.sensor_celsius = 22.0 raises AttributeError; use _replace() when a replacement record is appropriate. It also does not validate units, ranges, or types: 21.5 is meaningful only because this program treats it as Celsius. Field names must be valid identifiers and cannot begin with an underscore. For a changing record or richer validation, use a purpose-built class or dataclass instead.

AI assistance helped draft this article.

Source: Python namedtuple() documentation

Example

from collections import namedtuple

Measurement = namedtuple(
    "Measurement",
    "sensor_celsius taken_at",
    defaults=("unrecorded",),
)

row = Measurement(21.5)
assert row.sensor_celsius == 21.5
assert tuple(row) == (21.5, "unrecorded")

corrected = row._replace(sensor_celsius=22.0)
assert row.sensor_celsius == 21.5
assert corrected.sensor_celsius == 22.0

print(row)
print(row._asdict())
print(corrected)

Expected stdout

Measurement(sensor_celsius=21.5, taken_at='unrecorded')
{'sensor_celsius': 21.5, 'taken_at': 'unrecorded'}
Measurement(sensor_celsius=22.0, taken_at='unrecorded')