NamedTuples: An easier way
Python's namedtuples are a great way to write more readable code, but, if you've never heard about them, here's a quick summary.
Namedtuples quick explanation
Before talking about namedtuples, let's first talk about Tuples. They are one of the simplest Python's data structures, allowing you to store a sequence of items. The main feature of Tuples is that they are immutable (can't be modified once they are created). On the other hand, their main downside is that they can only be accessed from integer indexes, let's take a look at this dead simple example:
>>> person = ('Milton', 'Banana', 174) >>> type(person) <type 'tuple'> # Item accessing >>> print(person, person, person) ('Milton', 'Male', 174) # Immutability >>> person = 'Miguel' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment
You can go deeper about tuples in this link.
As you may think, data accessing through indexes might not be the most readable way in your code, also they could be cumbersome because you have to remember what does every index mean, a difficult task when your code gets older and older.
Fortunately, Python has another built-in data structure: Namedtuples
Trying to cover Tuple's downsides, Namedtuples were born. As its name says, they are the same tuples but with "names", this means that they have all Tuple's features but also it supports item accessing through unique (human-readable) identifiers. Let's rewrite our previous example with a namedtuple:
>>> from collections import namedtuple >>> Person = namedtuple('Person', ['name', 'age', 'height']) >>> person = Person('Milton', 25, 174) >>> person Person(name='Milton', age=25, height=174) # Item accessing >>> print(person.name, person.age, person.height) Milton 25 174 # Immutability >>> person.name = 'Milton' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: can't set attribute
If you want to know more about namedtuples click here
Namedtuples share all the benefits from Tuples, but also, they have better memory usage than regular classes. I like to think about namedtuples as Dan Bader described it in its good book Python Tricks:
namedtuples are a memory efficient shortcut to defining an immutable class in Python manually
I still think Namedtuples could be a lot better in terms of how you write them, for example, I don't really like the way you define them:
from collections import namedtuple Person = namedtuple('Person', ['name', 'age', 'height'])
I personally don't like to define a non-PEP-8-compliant variable (Because it doesn't respect the snake_case rule for variables) like
Person just to make it look like a class (Which is not mandatory but is just a good practice) and also I don't think it's clear what's happening when you pass
'Person' as a parameter (Which namedtuples use internally for the
A bigger problem I found with them is when you want to define optional-default values for their attributes. I went through this problem some months ago and this is what I found to handle it:
In Python 3.7
use the defaults parameter:
>>> from collections import namedtuple >>> Person = namedtuple( 'Person', ['name', 'age', 'height', 'ears', 'eyes'], defaults=(2, 2,) ) >>> Person('Milton', 25, 174) Person(name='Milton', age=25, height=174, ears=2, eyes=2)
Before Python 3.7
>>> from collections import namedtuple >>> Person = namedtuple( 'Person', ['name', 'age', 'height', 'ears', 'eyes'] ) >>> Person.__new__.__defaults__ = [2, 2] >>> Person('Milton', 25, 174) Person(name='Milton', age=25, height=174, ears=2, eyes=2) # Caitlyn had an accident :( >>> Person(name='Caitlyn', age=40, height=150, ears=1) # Note that you can also use kwargs Person(name='Caitlyn', age=40, height=150, ears=1, eyes=2)
In both cases, defaults are applied to the rightmost parameter (from right to left), that's why we could have
eyes with a default value but also some required parameters like
A total mess, isn't it?
The Easier Way
Fortunately, I discover a better alternative to handle this using Python 3.5+
Taking advantage about the fact that types and classes are equivalent and thus, produce equivalent instances, we could inherit from
NamedTuple and define our attributes same as in a regular class, this is how it could look like:
from typing import NamedTuple class Person(NamedTuple): # Type hints are optional, you don't have to use them but they are great! # I encorage you to learn more about them :) name: str age: int height: int ears: int = 2 eyes: int = 2 >>> milton = Person('Milton', 25, 174) >>> milton Person(name='Milton', age=25, height=174, ears=2, eyes=2) >>> caitlyn = Person(name='Caitlyn', age=25, height=174, ears=1) >>> caitlyn Person(name='Caitlyn', age=25, height=174, ears=1, eyes=2) # Immutability >>> milton.name = 'Miguel'
If you want to know more about Python's type checking I strongly recommend reading this great article from Real Python
See? you can write namedtuples using the simplicity of a regular class, but still benefit from immutability and memory-efficience from Tuples.
This will definitely make the use of Namedtuples a more enjoyable thing.