Up Your Coding Skills with Python: Named Tuples

Greetings! Today I’ll do my best to present what’s known as a named tuple. It is a class that’s based on the traditional tuple (see here for a brief info on tuples), but it’s one that adds some extra stuff on top of it, making it a very useful class to adopt in our code. And we’ll see together just why, in just a minute.
What are named tuples?
A simple question that begs a simple answer. And here it is: named tuples, as funny as it sounds, are, well… tuples :)
In terms of mutability, they’re also immutable, just like the tuples are, their elements can be accessed via integer indexing, such as my_tuple[2], they’re iterable, meaning you can go through each element of a named tuple, one by one, in other words, they have all that a regular tuple has.
The real difference is that we can also access elements via their names, very similar to how we can access the class’ attributes. This comes in very handy when you’d be dealing with large tuples, where you can’t be expected to remember what the 27th tuple element was representing, for example. It also greatly improves code readability, as it’s much better to read person.age — accessing the tuple’s element by its name, rather than the integer index — person[2].
We could look at the named tuple and see it providing similar functionality, ease of use, and code readability as if it were a class and the tuple elements were merely class attributes.
Creating a named tuple
# import namedtuple factory function
from collections import namedtuple# create a named tuple
person = namedtuple("person", ["first_name", "last_name", "age"])# instantiate the "person" namedtuple subclass
my_new_person = person("John", "Doe", "30")# Access using index
print(f"Age: {my_new_person[2]}")# Access using name
print(f"First name: {my_new_person.first_name}")# Access using getattr
print(f"Last name: {getattr(my_new_person, 'last_name')}")# Type
print(type(my_new_person))Output:
Age: 30
First name: John
Last name: Doe
<class '__main__.person'>As we’ve seen, the namedtuple() factory function took 2 arguments: the type — which is exactly what the type() method will return, as seen in the above example, alongside a list of names for the tuple elements. Once the named tuple has been created, or in other words, once the new named tuple subclass has been built, we can then instantiate it by supplying the necessary values to the person subclass __init__() method.
It’s very important to note that, as a named tuple, it is immutable. In other words, you can’t change the fields’ values, once supplied:
from collections import namedtupleperson = namedtuple("person", ["first_name", "last_name", "age"])
my_new_person = person("John", "Doe", "30")
my_new_person.first_name = "Daniel"Traceback (most recent call last):
...
my_new_person.first_name = "Daniel"
AttributeError: can't set attributeIt should also be noted that all elements are required. Here’s what would happen should we fail to provide all elements:
from collections import namedtupleperson = namedtuple("person", ["first_name", "last_name", "age"])
my_new_person = person("John", "Doe")Traceback (most recent call last):
...
my_new_person = person("John", "Doe")
TypeError: person.__new__() missing 1 required positional argument: 'age'Failing to supply all of the elements will earn us a TypeError exception.
Named tuple keyword arguments
The namedtuple() factory function features a couple of keyword arguments:
typename— we’ve already covered this, it’s the first argument that specifies the type of the class we’re building with thenamedtuple()factory function;field_names— we’ve also covered this, it’s the collection of field names. Doesn’t have to be alistof strings, it can also be a string containing multiple fields, separated by either,orSpace;
Here’s an example of both keyword arguments in action:
from collections import namedtupleperson = namedtuple(
typename="person",
field_names="first, last, age")my_new_person = person("John", "Doe", 30)print(f"Age: {my_new_person[2]}")
print(f"First name: {my_new_person.first}")
print(type(my_new_person))Output:
Age: 30
First name: John
<class '__main__.person'>rename— boolean value, specifying whether to rename forbidden field names (either to avoid field name duplication or collision with reserved keywords):
from collections import namedtupleperson = namedtuple(
typename="person",
field_names="first, first, else, age",
rename=True)my_new_person = person("John", "Andrew", "Doe", 30)print(my_new_person._fields)Output:
('first', '_1', '_2', 'age')The duplicate / collided field names were renamed using a prepending underscore character and an integer to differentiate between them.
defaults— helps to specify default values that will be assigned to the field names, should they be missing. Doesn’t need to be alist, iterables such as tuples also work. Sets would work too, but take note, as sets aren’t ordered collections, you may not get the right order of assignment as intended. Note that the defaults are being assigned starting with the rightmost field name:
from collections import namedtupleperson = namedtuple(
typename="person",
field_names="first, last, age, married, children",
defaults=["Not married", 0])my_new_person = person("John", "Doe", 30)
print(my_new_person.married)
print(my_new_person.children)Output:
Not married
0module— whether to also include the module name in which the respective named tuple has been defined. This impacts thetype()function return value:
from collections import namedtupleperson = namedtuple(
typename="person",
field_names=["first", "last", "age"],
module="my_module")my_new_person = person("John", "Doe", "30")print(f"Age: {my_new_person[2]}")
print(f"First name: {my_new_person.first}")
print(type(my_new_person))Output:
Age: 30
First name: John
<class 'my_module.person'>Without this keyword argument, the output of type(my_new_person) would simply be <class ‘__main__.person’>, the module defaulting to __main__.
Count occurrences of a value in named tuple
Using the count() method, we can count the occurrences of a value in a named tuple:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first_name", "last_name", "age"])# instantiate the person class
my_new_person = person("John", "Doe", "30")# count the "John" values
result = my_new_person.count("John")
print(result)# count a nonexistent value occurrences
result = my_new_person.count("Jack")
print(result)Output:
1
0Get the index of the first element having a certain value
The index() method returns the first (leftmost) index of the element having the value we’re after:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first_name", "last_name", "age"])# instantiate the person class
my_new_person = person("John", "John", "30")# get the index for "John" - this returns 0
result = my_new_person.index("John")
print(result)# get the index for a nonexistent value - this throws ValueError
result = my_new_person.index("Jack")
print(result)Output:
0
Traceback (most recent call last):
...
result = my_new_person.index("Jack")
ValueError: tuple.index(x): x not in tupleConvert a named tuple to a dictionary
I think we can safely assume we’ve already begun asking ourselves the following question: “Why would I use a named tuple when I can simply use a dictionary?”
Well, there can be multiple reasons for which you’d rather work with a named tuple as opposed to working with a dictionary:
- first off, the named tuples are immutable. So if you want to make sure your data will not (albeit accidentally) change, you’d probably be better off choosing a named tuple over a dictionary;
- secondly, named tuples are still ordered. While dictionaries are not ordered collections —if we’re discussing Python versions 3.6 and lower. Starting with Python 3.7, dictionaries are ordered, so this argument isn’t valid anymore;
The _asdict() method successfully assists us in obtaining a very useful OrderedDict object out of the equally useful named tuple:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first_name", "last_name", "age"])# instantiate the person class
my_new_person = person("John", "Doe", "30")# convert it to a dictionary
result = my_new_person._asdict()
print(result)Output:
{'first_name': 'John', 'last_name': 'Doe', 'age': '30'}Get named tuple fields
We can effectively get the fields of a named tuple by simply accessing the _fields attribute. The fields will be stored in a tuple:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first_name", "last_name", "age"])# instantiate the person class
my_new_person = person("John", "Doe", "30")# get the fields
result = my_new_person._fields
print(result)Output:
('first_name', 'last_name', 'age')Make a named tuple from an iterable
Creating a named tuple instance from an iterable is also supported, via the class method _make(). We just need to feed an iterable to it (one that has as many elements as the list of names the named tuple supports) and it will quickly create a named tuple instance for us, assigning the values of the iterable to those of the named tuple:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first_name", "last_name", "age"])# define the iterable containing the values
my_iterable = ["John", "Doe", "30"]# create the named tuple out of the iterable's values
my_new_person = person._make(my_iterable)
print(my_new_person)# attempt to create a named tuple out of an incompatible iterable
my_second_iterable = ["John", "Doe"]
my_next_person = person._make(my_second_iterable)Output:
person(first_name='John', last_name='Doe', age='30')
Traceback (most recent call last):
...
TypeError: Expected 3 arguments, got 2When we tried to create a named tuple instance containing 3 elements out of a 2-element iterable, we immediately found ourselves in error territory, courtesy of the all-too-familiar TypeError exception.
Create a named tuple out of a dictionary using the double star operator (**)
Very straightforward this, we just need to have the names for our named tuple and their corresponding values in the form of a dict and then we’ll be able to quickly create a named tuple out of that:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first", "last", "age"])# define the dictionary
dictionary = {"first": "John", "last": "Doe", "age": "30"}# create the named tuple
my_new_person = person(**dictionary)
print(my_new_person)Output:
person(first='John', last='Doe', age='30')Note that in order for this to work, the names of the named tuple class need to match the keys of the dictionary. Otherwise, we’d see a TypeError exception with a pretty clear message being thrown, letting us know of a mismatch between the expected field names and the actual dictionary keys.
Replacing values of a named tuple
Although we had established that named tuples were immutable, there’s still something we can do about modifying their values. Now, while named tuples’ immutable trait holds true, we have the _replace() method that will effectively create a different named tuple object in memory that will feature the desired changes:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first", "last", "age"])# instantiate the person class
my_new_person = person("John", "Doe", "30")# replace the first and last name and save into new object
my_other_person = my_new_person._replace(
first="Michael",
last="Adams")print(f"{id(my_new_person)}: {my_new_person}")
print(f"{id(my_other_person)}: {my_other_person}")Output:
140457992575856: person(first='John', last='Doe', age='30')
140457992576416: person(first='Michael', last='Adams', age='30')Just as we thought, the result does nothing to cancel named tuples’ immutability. Python just created another object in memory. It was our choice to assign it to another variable, because if we’d done something like this:
my_new_person = my_new_person._replace(
first="Michael",
last="Adams")we would have had the illusion of actual replacement of values. But in reality, we would have witnessed the actual replacement of objects:
from collections import namedtuple# create a named tuple subclass called person
person = namedtuple("person", ["first", "last", "age"])# instantiate the person class
my_new_person = person("John", "Doe", "30")
print(f"{id(my_new_person)}: {my_new_person}")# replace the first and last name and save into same object
my_new_person = my_new_person._replace(
first="Michael",
last="Adams")
print(f"{id(my_new_person)}: {my_new_person}")Output:
140439111032224: person(first='John', last='Doe', age='30')
140439111032864: person(first='Michael', last='Adams', age='30')Our named tuple object was replaced by another one, that would contain the changes we desired.
This is about all I had to share on named tuples in Python. While this article should provide a synthetic compilation of most things you can do with a named tuple, there’s always the official documentation link that will provide a great starting point to yet more knowledge on the topic.
Having said that, I wish you nothing but to always try to be the best version of yourself, in coding as in life and I’ll see you at the next one! Happy coding!
Deck is a software engineer, mentor, writer, and sometimes even a teacher. With 12+ years of experience in software engineering, he is now a real advocate of the Python programming language while his passion is helping people sharpen their Python — and programming in general — skills. You can reach Deck on Linkedin, Facebook, Twitter, and Discord: Deck451#6188, as well as follow his writing here on Medium.
More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter and LinkedIn. Check out our Community Discord and join our Talent Collective.






