# In Python, what are “named tuples”?

## Problem

When I was reading about the changes in Python 3.1, I came across something… unexpected:

I’d never heard of named tuples before, and I assumed that items could be indexed by numbers or by keys (like in tuples and lists) (like in dicts). I had no idea they could be indexed in both directions.

Thus, my questions are:

## Solution #1

Named tuples are lightweight object types that are simple to generate. Object-like variable dereferencing or standard tuple syntax can be used to refer to named tuple instances. Except that they are immutable, they can be used in the same way as struct or other common record types. Although there is a recipe for implementing them in Python 2.4, they were only added in Python 2.6 and Python 3.0.

It is usual to represent a point as a tuple, for example (x, y). As a result, code like this is generated:

``````pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
``````

It gets more readable when using a named tuple:

``````from collections import namedtuple
Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)
``````

Named tuples, on the other hand, are backwards compatible with regular tuples, therefore the following will still work:

``````Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
# use index referencing
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
# use tuple unpacking
x1, y1 = pt1
``````

As a result, if you think object notation will make your code more pythonic and legible, you should use named tuples instead of tuples. I’ve begun to use them to represent very basic value types, especially when sending them as parameters to functions. Without seeing the context of the tuple packing, it makes the functions more readable.

You may also use them to replace standard immutable classes that only contain fields and no functions. Your named tuple types can even be used as base classes:

``````class Point(namedtuple('Point', 'x y')):
[...]
``````

However, as with tuples, attributes in named tuples are immutable:

``````>>> Point = namedtuple('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
AttributeError: can't set attribute
``````

You’ll need a different type if you wish to change the values. For mutable recordtypes, there is a useful recipe that allows you to change the values of attributes.

``````>>> from rcdtype import *
>>> Point = recordtype('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
>>> print(pt1[0])
2.0
``````

However, I’m not aware of any type of “named list” that allows you to add new fields. In this case, you might only wish to consult a dictionary. pt1. asdict() converts named tuples to dictionaries, which return ‘x’: 1.0,’y’: 5.0 and can be used with all of the standard dictionary functions.

As previously said, you should consult the documentation for further information on which these examples were based.

## Solution #2

A named tuple is just that: a tuple.

It can do all of the functions that a tuple can.

It’s more than a tuple, though.

It’s a subclass of tuple with named fields and a defined length that’s built programmatically to your specifications.

This, for instance, produces a tuple subclass that, aside from having a set length (in this case, three), can be used anywhere a tuple can be used without breaking. Liskov substitutability is the term for this.

We can now utilize a class definition with typing in Python 3.6. To make a namedtuple, use NamedTuple:

``````from typing import NamedTuple

class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list
``````

The above is identical to the below, with the exception that the above includes type annotations and a docstring. Python 2+ has the following features:

``````>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)
``````

This instantiates it:

``````>>> ant = ANamedTuple(1, 'bar', [])
``````

We can look at it and use its properties:

``````>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']
``````

To comprehend named tuples, you must first comprehend what a tuple is. A tuple is effectively an immutable list (one that cannot be altered in memory).

A regular tuple can be used in the following ways:

``````>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'
``````

Iterable unpacking can be used to expand a tuple:

``````>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
'A'
``````

Named tuples are tuples that allow you to retrieve their elements by name rather than index!

You create a namedtuple in the following way:

``````>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])
``````

A little more readable use of the API is to use a single string with the names separated by spaces:

``````>>> Student = namedtuple('Student', 'first last grade')
``````

You can perform everything that tuples can do (see above), plus the following:

``````>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple
``````

The namedtuple types are essentially classes that may be created with simple shorthand. Treat them as if they were classes. Pickle and other users will be able to find them if they are defined at the module level.

The following is an example of a working example at the global module level:

``````>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
NT(foo='foo', bar='bar')
``````

And this exemplifies the inability to look up a definition:

``````>>> def foo():
...     LocalNT = namedtuple('LocalNT', 'foo bar')
...     return LocalNT('foo', 'bar')
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed
``````

When having the semantics of tuple components explicit in your code benefits your code, use them.

If you would otherwise use an object with unchanging data characteristics and no functionality, you can use them instead.

You can also add functionality to them by subclassing them, for example:

``````class Point(namedtuple('Point', 'x y')):
"""adding functionality to a named tuple"""
__slots__ = ()
@property
def hypot(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)
``````

When the tuple is employed, readability is proven.

Unlike tuples, named tuples do not consume any additional memory.

You’re seeking for a subclassed list that functions like a named tuple or a slotted object that implements all of the features of a statically sized list (and that somehow blocks the list from changing in size.)

An enlarged (and maybe Liskov-substitutable) version of the first:

``````from collections import Sequence

class MutableTuple(Sequence):
"""Abstract Base Class for objects that work like mutable
namedtuples. Subclass and define your named fields with
__slots__ and away you go.
"""
__slots__ = ()
def __init__(self, *args):
for slot, arg in zip(self.__slots__, args):
setattr(self, slot, arg)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
# more direct __iter__ than Sequence's
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
# Sequence requires __getitem__ & __len__:
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
``````

To use, simply create a subclass and define __slots__:

``````class Student(MutableTuple):
__slots__ = 'first', 'last', 'grade' # customize

>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
...
Bart
Simpson
A
``````

## Solution #3

namedtuple is a function that creates a tuple class. We can generate tuples that are also callable by name using that class.

``````import collections

#Create a namedtuple class with names "a" "b" "c"
Row = collections.namedtuple("Row", ["a", "b", "c"])

row = Row(a=1,b=2,c=3) #Make a namedtuple from the Row class we created

print row    #Prints: Row(a=1, b=2, c=3)
print row.a  #Prints: 1
print row[0] #Prints: 1

row = Row._make([2, 3, 4]) #Make a namedtuple from a list of values

print row   #Prints: Row(a=2, b=3, c=4)
``````

## Solution #4

namedtuples are a fantastic feature; they’re the ideal data container. When you need to “save” data, tuples or dictionaries, such as:

``````user = dict(name="John", age=20)
``````

or:

``````user = ("John", 20)
``````

Because dicts are mutable and slower than tuples, the dictionary method is overpowering. Tuples, on the other hand, are immutable and lightweight, but they are difficult to understand for a large number of data fields.

namedtuples are an excellent balance between the two approaches because they are easy to read, lightweight, and immutable (plus they are polymorphic!).

## Solution #5

Backward compatibility with code that checks for the version, such as this, is possible using named tuples.

``````>>> sys.version_info[0:2]
(3, 1)
``````

While using this approach, future code will be more explicit.

``````>>> sys.version_info.major
3
>>> sys.version_info.minor
1
``````