Topics: Immutable classes, namedtuples
Updated 2020-10-04

Tuples

Before looking at namedtuples, we should review what a tuple is. In Python, a tuple is a simple data structure that can be used for grouping arbitrary objects. Important to know is that tuples are immutable. That means that once a tuple has been created, it can not be changed anymore. We already used tuples in our Magical Universe. For example, we defined the pet attribute of the Pupil class to be a tuple:

pet = ('name', 'type')
lissys_pet = ('Ramses', 'cat')

The pet tuple has two fields: name and type that can be accessed using their integer index. Once we create a tuple like lissys_pet we can’t change it anymore. For example, running

lissys_pet[0] = 'Twiggles'

throws a TypeError: 'tuple' object does not support item assignment.

Namedtuples

As their name suggests, namedtuples are a variation (or rather extension) of plain tuples. In particular, namedtuples allow us to name the fields of the tuple. This makes it much easier to access the individual fields. Also, it makes our code more readable. In the plain tuple example above, we could access the values stored in the tuple only by using integer indices, as lissys_pet[0] or lissys_pet[1]. When having only two fields this is not too bad. But with more than three fields things become messy.

Creating a namedtuple is easy. There are two kinds of namedtuples we can use: collections.namedtuple or typing.NamedTuple. When using collections.namedtuple our pet tuple would be defined as follows:

from collections import namedtuple
Pet = namedtuple('Pet', ['name, type'])
lissys_pet = Pet('Ramses', 'cat')

We can access the fields of the namedtuple using the field names or their indices:

name = lissys_pet[0]
# or
name = lissys_pet.name

typing.NamedTuple has a slightly different syntax and allows us to specify the type of each field. It also allows us to add methods to the class (although this is possible with collections.namedtuple, too, it’s a little more complex). Another advantage of the namedtuple classes is that they come with an implementation of the __repr__ method. So we don’t have to create that ourselves anymore but get a nice string representation of our objects right away (as you can see in the example below).

from typing import NamedTuple

class Pet(NamedTuple):
    name: str
    type: str

lissys_pet = Pet('Ramses', 'cat')
print(lissys_pet)
>>> Pet(name='Ramses', type='cat')

You can read more about typing.NamedTuple in the Python docs.

Dark Army class

We don’t want our pupils, professors and ghosts to be immutable. A suitable group of people for an immutable class are the Dark Army members. Why? Because once you become a member of the Dark Army there is now way back. So let’s create a NamedTuple for them!

from typing import NamedTuple

class DarkArmyMember(NamedTuple):
    name: str
    birthyear: str

    @property
    def leader(self) -> 'DarkArmyMember':
        master_odon = DarkArmyMember('Master Odon', 1971)
        return master_odon

From now on we can easily create new Dark Army members:

keres = DarkArmyMember('Keres Fulford', 1983)

print(keres)
>>> DarkArmyMember(name='Keres Fulford', birthyear=1983)

print(keres.leader)
>>> DarkArmyMember(name='Master Odon', birthyear=1971)

Data classes

Note: When using Python 3.7 we can also use dataclasses for creating immutable classes! Data classes are discussed in more detail on day 16 to 18.