Empower a Lightweight Python Data Structure: From Tuples to Namedtuples
Take advantage of named tuples to manage your data
No matter what languages you use and what products you make, it’s inevitable that your programs have to deal with data. As a general-purpose programming language, Python provides us with a wide collection of built-in data types and thus gives us plenty of flexibility in how to store, pass, and update data between different components of our programs.
Tuple
When it comes to dealing with related data elements, one of the most commonly used data types is the tuple. As an immutable data type, tuples are sequences of data with a fixed size. They’re useful to group related data with different data types. Consider the following trivial example.
>>> employee0 = ('John Smith', 45, 'M', 160083)In the above code snippet, we define a tuple called employee0, which stores an employee’s personal data, including name, age, gender, and employee ID number. If we need to use some elements of the tuple, we can unpack it or use subscript, and their usages are shown below.
>>> # Use unpacking
>>> name, age, gender, employee_id = employee0
>>> print(f"Employee Name: {name}")
Employee Name: John Smith
>>>
>>> # Use subscript
>>> print(f"Employee Age: {employee0[1]}")
Employee Age: 45
>>> print(f"Employee ID #: {employee0[-1]}")
Employee ID #: 160083How about we need to deal with another employee in the same module? We’ll have to do something like below.
>>> # Create a tuple for storing another employee data
>>> employee1 = ('Jennifer Brown', 38, 'F', 150384)
>>>
>>> # Access data
>>> name1, age1, gender1, employee_id1 = employee1
>>> print(f"Employee Name: {employee1[0]}")
Employee Name: Jennifer Brown
>>> print(f"Employee Age: {age1}")
Employee Age: 38
>>> print(f"Employee ID #: {employee_id1}")
Employee ID #: 150384In essence, we have to repeat the above steps, and access individual elements using either unpacking or subscripts, which certainly is not the most pleasant thing to do. Actually, it can be error-prone because you have to remember the exact order of these data.
Custom Class
Is there any better solution? Certainly, your first instinct may be to create a custom class to hold these data. The solution will be like something below.
>>> # Define a custom class
>>> class Employee:
... def __init__(self, name, age, gender, employee_id):
... self.name = name
... self.age = age
... self.gender = gender
... self.employee_id = employee_id
...
>>> # Create an instance of Employee class
>>> employee2 = Employee('David Berger', 35, 'M', 134039)
>>> # Access data
>>> print(f"Employee Name: {employee2.name}")
Employee Name: David Berger
>>> print(f"Employee Age: {employee2.age}")
Employee Age: 35
>>> print(f"Employee ID #: {employee2.employee_id}")
Employee ID #: 134039It’s definitely acceptable to create a custom class to manage these employee data. However, there are lots of overheads to declare and manage a class. It is a little bit cumbersome for our data storage and reading purpose.
In addition, the data are subject to unintentional changes, as shown below. We don’t want it to happen because our goal is to simply hold the employees’ information and access them conveniently.
>>> # Change the employee ID number
>>> employee2.employee_id = 500
>>> print(f"Employee ID #: {employee2.employee_id}")
Employee ID #: 500Namedtuple
Class and Instance Creation
The namedtuples come to the rescue. With namedtuples, we can assign specific field names to individual positions in regular tuples such that we will have more readable code. Before we expand our discussion on namedtuples, let’s see how we can construct it.
>>> from collections import namedtuple
>>>
>>> # Construct a namedtuple class
>>> Employee = namedtuple('Employee', ['name', 'age', 'gender', 'employee_id'])
>>>
>>> # Create an instance
>>> employee = Employee('Ann Luck', 28, 'F', 193080)
>>>
>>> # Introspection
>>> type(employee)
<class '__main__.Employee'>
>>> isinstance(employee, Employee)
True
>>> isinstance(employee, tuple)
TrueThere are a few things to note in the above code snippet.
- The namedtuple type is available in the collections module.
- We construct the
Employeeclass using the factory functionnamedtuple(), which takes a string as the class name and a list of strings to denote the fields for the class. - Once the namedtuple class is created, we can create an instance object as we normally do with a regular custom class as we did before.
- The instance object (i.e.,
employee) is of the typeEmployeeclass. In addition, the Employee class is a subclass of thetupledata type, and thus the objectemployeeis also an instance of thetuple.
We have created the class and the instance object above. Now accessing individuals’ attributes is much more straightforward with the dot notation, like what we do with custom classes, as shown below.
>>> # Create an instance
>>> employee01 = Employee('Bella Jones', 42, 'F', 178394)
>>>
>>> # Access data
>>> print(f"Employee Name: {employee01.name}")
Employee Name: Bella Jones
>>> print(f"Employee Age: {employee01.age}")
Employee Age: 42
>>> print(f"Employee ID #: {employee01.employee_id}")
Employee ID #: 178394Data Manipulation
Although we showed that namedtuples are a subclass of tuples, it is actually a more flexible data type than tuples with additional data manipulation methods. The most common methods are _make, _asdict, and _replace. Let’s review these methods and see their respective usages below.
classmethod somenamedtuple._make(iterable). This is a class method that creates the namedtuple instance object using an existing iterable.
>>> # instance from a tuple
>>> t0 = ('Bella Jones', 42, 'F', 178394)
>>> employee_t0 = Employee._make(t0)
>>>
>>> # instance from a list
>>> t1 = ['Jerry Dani', 38, 'M', 170438]
>>> employee_t1 = Employee._make(t1)somenamedtuple._asdict(). This is an instance method that creates a dictionary presentation of the instance object. One thing to note is that the returned value of this function has changed between different versions of Python. In Python 3.7, the returned type isOrderedDict, but in Python 3.8+, the returned type isdict. Nevertheless, we can conveniently convert anOrderedDicttodictusing thedict()constructor function.
>>> # Create an instance
>>> employee02 = Employee('Cathy Bradley', 44, 'F', 180030)
>>>
>>> # Get the dictionary representation
>>> employee02._asdict()
OrderedDict([('name', 'Cathy Bradley'), ('age', 44), ('gender', 'F'), ('employee_id', 180030)])
>>>
>>> # Convert it to a regular dict
>>> dict(employee02._asdict())
{'name': 'Cathy Bradley', 'age': 44, 'gender': 'F', 'employee_id': 180030}somenamedtuple._replace(**kwargs). This is an instance method that returns a new instance object with the replacement of specified fields with the new values. One thing to note that because of the immutability of tuples, replacing one field won’t update the original tuple object. As shown below, we update the objectemployee03’s age, but when we check the age, which is still the original value of 58.
>>> # Create an instance
>>> employee03 = Employee('David Bradley', 58, 'M', 150030)
>>>
>>> # Replace the age
>>> employee03._replace(age=59)
Employee(name='David Bradley', age=59, gender='M', employee_id=150030)
>>> employee03.age
58
>>>
>>> # Create a new instance by replacing the age
>>> employee03 = employee03._replace(age=59)
>>> dict(employee03._asdict())
{'name': 'David Bradley', 'age': 59, 'gender': 'M', 'employee_id': 150030}Conclusions
In this article, we reviewed the nametuples as a lightweight alternative data structure to handle data. In essence, they can be used wherever regular tuples are applicable. Importantly, they allow us to access fields by names instead of their elements’ indices, a feature that makes our code more readable.
Additional Reading List
There are a few concepts that are related to the present article, including tuples, data mutability, and certainly, nametuples. The following references are for your convenience if you want to learn more about these topics.





