Named tuples (collections.namedtuple) are a very convenient way of gathering data together so it can be passed around. They provide a guaranteed data structure and parts can be referenced by name. It is even possible to set defaults, albeit in a clunky way. And they’re fast and lightweight (see https://death.andgravity.com/namedtuples). What more could you want!
Example:
"""
Process Points Of Interest (POIs)
“””from collections import namedtuple
POI = namedtuple('POI', 'x, y, lbl')pois = []
for … :
pois.append(POI(x, y, city))
… (maybe in a different module)
for poi in pois:
logging.info(f"{poi.lbl}")
.lbl
will always refer to a label and I can add more fields to POI in future and this code here will still work. If I change lbl to label in POI this will break hard and obviously.
An alternative is data classes – a more readable version of standard named tuples that makes defaults much easier to use. Hmmmm – maybe these are better. They arrived in 3.7 so instead of:
from collections import namedtuple
## Note - defaults apply to rightmost first
POI = namedtuple('POI', 'x, y, lbl', defaults=['Unknown'])
We can now use:
from dataclasses import dataclass
@dataclass(frozen=True, order=True)class POI:
x: float
y: float
lbl: str = 'Unknown'
The readable type hinting is nice too and the data structure self-documents.
Not bad but I have concluded data classes are generally an inferior version of what we can make with typing.NamedTuple. Instead of:
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class POI:
x: float
y: float
lbl: str = 'Unknown'
We can use:
from typing import NamedTuple
class POI(NamedTuple):
x: float
y: float
lbl: str = 'Unknown'
which has immutability and ordering out of the box not to mention having less boilerplate.
Data classes are only superior as far as I can see when mutability is wanted (frozen=False).
Another black mark against data classes – you have to import asdict
from dataclasses
to create a dictionary whereas namedtuples (and NamedTuples) have the _asdict()
method built in.
Note – it very easy to swap from NamedTuple to dataclass should the need for mutability ever arise – all the field definitions are the same.
So the verdict is in favour of typing.NamedTuple
.
That’s a lot of detail on something minor but I do use small data structures of this sort a lot to make code safer and more readable. Vanilla tuples are usually dangerous (they force you to use indexing referencing which is brittle and unreadable); and dictionaries require consistent use of keys and it is potentially painful to change keys. NamedTuples seem like a perfect balance in Python. Make them a standard part of your coding practice.