Why Lists Should Be Homogeneous but Tuples Can Be Heterogeneous?

In Python, a list can hold objects of any type. A list is a mutable container, which means that its elements can be replaced during program execution, and the number of elements can be increased or decreased.

By contrast, a tuple is an immutable container. Once a particular instance of a tuple has been created, its elements cannot be replaced, and the number of elements cannot be increased or decreased.

Although the elements of a list are not required to be of the same type, it is generally advisable for them to be homogeneous, because lists are mutable. In that case, after adding, removing, or replacing elements in the list, there is no need to check each element individually when using them. We can rely on the assumption that every element at every index position supports the operation we want to perform. This is what we mean when we say that the elements are homogeneous.

The following line of code illustrates this idea: a function receives a list as input. It iterates through the elements, adding 1 to the value of the first element and printing the result. After that, the first element is removed. The procedure continues as long as the list still contains elements.

In this case, element homogeneity means that 1 can be added to every element. For the function to work correctly under this definition, this homogeneous characteristic is necessary, because one operand of the addition—the element in the first position (index 0)—will always change.

Since a tuple is an immutable container, neither the elements nor their index positions can be changed. So, there is no risk that an element with different characteristics will appear at a given index position than the one that was there when the tuple was created. Therefore, the elements of a tuple may be heterogeneous: the element at a given index always remains the same, so it can be processed in a predictable way.

When we referred to elements with homogeneous characteristics, we deliberately did not say that elements of the same type. During processing, we typically want to use one or more predefined data or method attributes of the elements. For this, identical types are not necessarily required. For example, int and float are different types, yet both provide the real and imag data attributes, as well as the as_integer_ratio() method. Therefore, if we want to use these attributes, a list may contain both int and float values.

The situation becomes slightly different if we annotate a variable — or even a function parameter or return value — that references a list, to indicate the type of its elements. In this case, when using the list[] annotation, we must specify a type inside the square brackets—and only one can be given, since homogeneity is expressed through a single type.

For example, if we want to annotate the variable annotated_list shown in the example, the type hint can be either list[int] or list[float]. Therefore, if we concatenate a list containing int elements with another list containing float elements and assign the result to the variable annotated_list, a static type checker (e.g., mypy) will report an error—even though processing elements of different types would not actually cause any problems.

This topic is discussed in more detail in the chapter “Type Hints Using Generic Types” in the e-book Python Knowledge Building Step by Step from the Basics to the First Desktop Application.

Interested in the e-book Python Knowledge Building Step by Step: From the Basics to Your First Desktop Application?