avatarYong Cui

Summarize

Photo by Devin Avery on Unsplash

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 #: 160083

How 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 #: 150384

In 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 #: 134039

It’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 #: 500

Namedtuple

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)
True

There are a few things to note in the above code snippet.

  • The namedtuple type is available in the collections module.
  • We construct the Employee class using the factory function namedtuple(), 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 type Employee class. In addition, the Employee class is a subclass of the tuple data type, and thus the object employee is also an instance of the tuple.

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 #: 178394

Data 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 is OrderedDict, but in Python 3.8+, the returned type is dict. Nevertheless, we can conveniently convert an OrderedDict to dict using the dict() 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 object employee03’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.

Technology
Artificial Intelligence
Programming
Software Engineering
Python
Recommended from ReadMedium