What's the difference between enum and namedtuple?

Question:

I would like to know what are the differences between enum and namedtuple and when one should use one over the other.

Asked By: Carlos Afonso

||

Answers:

As an analogy (albeit an imperfect one), you can think of enum.Enum and namedtuple in python as enum and struct in C. In other words, enums are a way of aliasing values, while namedtuple is a way of encapsulating data by name. The two are not really interchangeable, and you can use enums as the named values in a namedtuple.

I think this example illustrates the difference.

from collections import namedtuple
from enum import Enum

class HairColor(Enum):
    blonde = 1
    brown = 2
    black = 3
    red = 4

Person = namedtuple('Person', ['name','age','hair_color'])
bert = Person('Bert', 5, HairColor.black)

You can access the named “attributes” of the person the same way you would a regular object.

>>> print(bert.name)
Bert
>>> print(bert.age)
5
>>> print(bert.hair_color)
HairColor.black
>>> print(bert.hair_color.value)
3

You often don’t see namedtuples like this because the same essential concept can be accomplished by using the more widely known class declaration. The class definition below behaves almost identically to the namedtuple definition above.

class Person:
    def __init__(self, name, age, hair_color):
        self.name = name
        self.age = age
        self.hair_color = hair_color

However, a major difference between a namedtuple and a class object is that the attributes of a namedtuple cannot be changed after its creation.

Answered By: Billy

The namedtuple is a fast structure that, using __slots__ instead of __dict__, finalizes the content you provide at initialization (that practically becomes read only, though a _replace() method exists).

A namedtuple is generally used when you need many (such as hundreds, thousands and even millions of) objects of the same type or you are reading and/or writing a record.
For instance, an often cited example is a Point namedtuple that might be used to work with a polygon vertex with its x, y, z components.
The overhead introduced by a namedtuple over a regular tuple is minimal if compared to the benefit of always pointing to the right component by name (.x, .y, .z, …) instead of by index (0, 1, 2, …).
Reading code such as A.x is easier than A[0]: the meaning is evident, even months after you have written the code and, better, for other programmers as well.

Thus a namedtuple is fast, can be used to meaningfully identify the content of the tuple and, last but not least, may coexist with older code accessing a tuple content by index.

from collections import namedtuple

Point = namedtuple('Point', 'x y z')  # note the x, y, z fields

origin = Point(0, 0, 0)

A = Point(1, 1, 1)
B = Point(1, 1, 0)
C = Point(1, 0, 0)
D = Point(1, 2, 3)

for p in (origin, A, B, C, D):
    print(p)
    print('x:', p.x, '  y:', p.y, '  z:', p.z)
    print('x:', p[0], '  y:', p[1], '  z:', p[2])
    print()

Going on from the example above, as soon as everything is accessing the points components by name instead of by index, further changes might be more easily introduced, by not going into changing any index number:

from collections import namedtuple


Point = namedtuple('Point', 'name x y z')  # addition of the field 'name'

origin = Point('O', 0, 0, 0)

A = Point('A', 1, 1, 1)
B = Point('B', 1, 1, 0)
C = Point('C', 1, 0, 0)
D = Point('D', 1, 0, 1)

for p in (origin, A, B, C, D):
    print(p)
    print(p.name)  # more readable than p[0] that is no more the x coordinate
    print('x:', p.x,  '  y:', p.y,  '  z:', p.z)  # unchanged
    print('x:', p[1], '  y:', p[2], '  z:', p[3])  # changed
    print()

An enumeration is a way to couple symbolic names to constant values and classify them as a specific set. We define an enumeration by creating a class derived from either Enum or IntEnum, depending on the values we want our constants to have: Enum is the generic version, IntEnum enforces the fact that every constant value will be of type int.

For instance, enums are good for defining colors by name, specific integer types, gender, or, again, – more generally – elements belonging to a specific set.

from enum import Enum, IntEnum, unique

class Color_1(Enum):
    red = 'red'
    green = 'green'
    blue = 'blue'

class Color_2(Enum):
    red = (255, 0, 0)
    green = (0, 255, 0)
    blue = (0, 0, 255)

class Color_3(IntEnum):
    red = 0xFF0000
    green = 0xFF00
    blue = 0xFF

class Gender_1(Enum):
    unknown = 'U'
    male = 'M'
    female = 'F'

class Gender_2(Enum):
    unknown = 0.3
    male = 0.5
    female = 0.7

class Shape(Enum):  # Note the different constants types, perfectly legal
    TRIANGLE = 't'
    RECTANGLE = 5
    SQUARE = tuple('square')

class DataType(IntEnum):
    int8 = -8
    int16 = -16
    int32 = -32
    int64 = -64
    int = -2
    negative = -1
    positive = 1
    uint = 2
    uint8 = 8
    uint16 = 16
    uint32 = 32
    uint64 = 64

In the pythonic development – enumerations elements may have a specific value assigned – that can be either unique or not, depending on your preference and specification. The unique decorator is used to enforce values uniqueness. By default, it is possible to assign the same constant value to two or more different symbolic names.

class Color_4(IntEnum):
    red = 1
    green = 2
    blue = 3
    RED = 1
    GREEN = 2
    BLUE = 3

Enumerations elements can be compared with each other, but for them to be successful, not only must the value match, even their type must be the same.

For instance:

Color_4.red == Color_4.RED

will return True (same class, same value), but the following:

Shape.SQUARE == tuple('square')

will be False – because the right element of the comparison – tuple(‘square’) – is not of type Shape, though both of them have the same value.

To conclude, enums and namedtuples are different instruments.

Enumerations have been added just recently to Python (search PEP435). If memory serves me right, namedtuples were available for quite a long time, but I am still a community newbie, thus I may be wrong.
HTH

Answered By: Alberto Vassena