{"id":1524,"date":"2022-04-20T18:42:42","date_gmt":"2022-04-20T06:42:42","guid":{"rendered":"https:\/\/p-s.co.nz\/wordpress\/?p=1524"},"modified":"2022-04-20T18:42:43","modified_gmt":"2022-04-20T06:42:43","slug":"python-named-tuples-vs-data-classes","status":"publish","type":"post","link":"http:\/\/p-s.co.nz\/wordpress\/python-named-tuples-vs-data-classes\/","title":{"rendered":"Python Named Tuples vs Data Classes"},"content":{"rendered":"\n<p>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&#8217;re fast and lightweight (see <a rel=\"noreferrer noopener\" href=\"https:\/\/death.andgravity.com\/namedtuples\" target=\"_blank\">https:\/\/death.andgravity.com\/namedtuples<\/a>). What more could you want!<br><br>Example:<\/p>\n\n\n\n<p><code>\"\"\"<\/code><br><code>Process Points Of Interest (POIs)<\/code><br>&#8220;&#8221;&#8221;<br><code>from collections import namedtuple<br>POI = namedtuple('POI', 'x, y, lbl')<\/code><br><code>pois = []<br>for \u2026 :<br>pois.append(POI(x, y, city))<br>\u2026 (maybe in a different module)<br>for poi in pois:<br>logging.info(f\"{poi.lbl}\")<\/code><\/p>\n\n\n\n<p><code>.lbl<\/code> 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.<\/p>\n\n\n\n<p>An alternative is data classes &#8211; a more readable version of standard named tuples that makes defaults much easier to use. Hmmmm &#8211; maybe these are better. They arrived in 3.7 so instead of:<\/p>\n\n\n\n<p>f<code>rom collections import namedtuple<\/code><br><code>## Note - defaults apply to rightmost first<br>POI = namedtuple('POI', 'x, y, lbl', defaults=['Unknown'])<\/code><\/p>\n\n\n\n<p>We can now use:<\/p>\n\n\n\n<p><code>from dataclasses import dataclass<br>@dataclass(frozen=True, order=True)<\/code><br><code>class POI:<br>    x: float<br>    y: float<br>    lbl: str = 'Unknown'<\/code><\/p>\n\n\n\n<p>The readable type hinting is nice too and the data structure self-documents.<\/p>\n\n\n\n<p>Not bad but I have concluded data classes are generally an inferior version of what we can make with typing.NamedTuple. Instead of:<\/p>\n\n\n\n<p><code>from dataclasses import dataclass<\/code><br><code>@dataclass(frozen=True, order=True)<br>class POI:<br>\u00a0\u00a0\u00a0 x: float<br>\u00a0\u00a0\u00a0 y: float<br>\u00a0\u00a0\u00a0 lbl: str = 'Unknown'<\/code><\/p>\n\n\n\n<p>We can use:<\/p>\n\n\n\n<p><code>from typing import NamedTuple<br>class POI(NamedTuple):<br>\u00a0\u00a0\u00a0 x: float<br>\u00a0\u00a0\u00a0 y: float<br>\u00a0\u00a0\u00a0 lbl: str = 'Unknown'<\/code><\/p>\n\n\n\n<p>which has immutability and ordering out of the box not to mention having less boilerplate.<\/p>\n\n\n\n<p>Data classes are only superior as far as I can see when mutability is wanted (frozen=False).<\/p>\n\n\n\n<p>Another black mark against data classes &#8211; you have to import <code>asdict<\/code> from <code>dataclasses<\/code> to create a dictionary whereas namedtuples (and NamedTuples) have the<code> _asdict()<\/code> method built in.<\/p>\n\n\n\n<p>Note &#8211; it very easy to swap from NamedTuple to dataclass should the need for mutability ever arise &#8211; all the field definitions are the same.<\/p>\n\n\n\n<p>So the verdict is in favour of <code>typing.NamedTuple<\/code>.<\/p>\n\n\n\n<p>That&#8217;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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &hellip; <a href=\"http:\/\/p-s.co.nz\/wordpress\/python-named-tuples-vs-data-classes\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[21,18,15,25],"class_list":["post-1524","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-code","tag-programming","tag-python","tag-python3"],"_links":{"self":[{"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/posts\/1524"}],"collection":[{"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/comments?post=1524"}],"version-history":[{"count":1,"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/posts\/1524\/revisions"}],"predecessor-version":[{"id":1525,"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/posts\/1524\/revisions\/1525"}],"wp:attachment":[{"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/media?parent=1524"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/categories?post=1524"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/p-s.co.nz\/wordpress\/wp-json\/wp\/v2\/tags?post=1524"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}