Beginner

Mutability.


In python, some objects are mutable (can be modified) while some other objects are immutable (cannot be modified).

For collections, this mean that some collecions are "locked" and elements cannot be added to it nor its existing elements can be modified.


Lists Tuples Sets Dictionaries
Mutable Immutable Mutable Mutable

ToC

In [1]:
l1 = [1, 2, 3, 4, 5]

l1.append(6)

l1[0] = 8

print(l1)
[8, 2, 3, 4, 5, 6]

After creating a list, we can add elements and change the already existing elements.


Now let's look at what happen with an immutable collection.

In [2]:
t1 = (1, 2, 3, 4, 5)

try:
    t1[0] = 5
except TypeError as e:
    print(f"Error : {e}")
Error : 'tuple' object does not support item assignment

Tuples don't have append method and we can see that trying to acces an element in order to change it return an error.

Intermediate

Now you may have tried something like t1 += (6, 7) and wondered why it works.

In [3]:
t1 += (6, 7)

print(t1)
(1, 2, 3, 4, 5, 6, 7)

It seems like it should not work, it looks like t1 was actually modified but it is not the case.

In fact we can look at the actual memory address of the tuple to understand what happens.

In [4]:
t1 = (1, 2, 3, 4, 5)

print(f"Address of t1 before {hex(id(t1))}.")

t1 += (6, 7)

print(f"Address of t1 before {hex(id(t1))}.")
Address of t1 before 0x7f9e49984ae0.
Address of t1 before 0x7f9e38090220.

Indeed, t1 before and after the modification is not the same object anymore. A new object with all the values contained in t1 and the new values is created an assigned to the old variable.

Advanced

There is another subtilty that is worth mentioning with immutable collections :

Immutable collections can actually contain mutable collections.

In [5]:
t1 = (1, 2, [3, 4])

t1[2][0] = 5

print(t1)
(1, 2, [5, 4])

Here we can sse that an element was modified inside of a tuple.

Once again let's look at the memory addresses to get a better understanding of what is happening.

In [6]:
t1 = (1, 2, [3, 4])

print(f"Address of t1 before {hex(id(t1))}.")
print(f"Address of the list inside t1 before {hex(id(t1[2]))}.")

t1[2][0] = 5

print(f"\nAddress of t1 before {hex(id(t1))}.")
print(f"Address of the list inside t1 before {hex(id(t1[2]))}.")
Address of t1 before 0x7f9e49985fc0.
Address of the list inside t1 before 0x7f9e3805d300.

Address of t1 before 0x7f9e49985fc0.
Address of the list inside t1 before 0x7f9e3805d300.

The important thing to understand is that the 3rd element of our tuple is not the actual list itself but rather a reference to this list. When applying a change to this list, the reference to this list is not changed, and that is why this example is possible.

Beginner

Ordered.


Some collection are ordered and some are not. The difference is subtle and may not impact a lot of code, but it is important to know that some collections are unordered because it can yield some unexpected results in some specific cases.


Lists Tuples Sets Dictionaries
Ordered Ordered Unordered Unordered

ToC

In [7]:
l1 = [1, 'Word', 5, 2, 'A few more words']

print(l1)
[1, 'Word', 5, 2, 'A few more words']
In [8]:
s1 = set('a word')

print(s1)
{'r', 'd', ' ', 'o', 'a', 'w'}

We can see that the elements of the set are indeed not ordered.

Beginner

Subscriptability.


Some collection are ordered and some are not. The difference is subtle and may not impact a lot of code, but it is important to know that some collections are unordered because it can yield some unexpected results in some specific cases.

Usually, unorderd collection are not subscriptable, except for dictionary type collection that use keys.


Lists Tuples Sets Dictionaries
Subscriptable / Slicable Subscriptable / Slicable Not subscriptable / Not slicable Subscriptable / Not slicable

ToC

In [9]:
l1 = [1, 2, 3, 4, 5]

print(f"Getting an element by index : {l1[0]}")

print(f"Getting an element by slice : {l1[:2]}")
Getting an element by index : 1
Getting an element by slice : [1, 2]
In [10]:
s1 = {1, 2, 3, 4, 5}

try:
    print(f"Getting an element by index : {s1[0]}")
except TypeError as e:
    print(f"Error: {e}")
Error: 'set' object is not subscriptable
In [11]:
d1 = {'A': 1, 'B': 2, 'C': 3}

print(f"Getting an element by key : {d1['A']}")

try:
    print(f"Getting an element by slice : {d1[:2]}")
except TypeError as e:
    print(f"Error: {e}")
Getting an element by key : 1
Error: unhashable type: 'slice'

Beginner

Duplicated elements.


Most collection accept duplicated elements but some, such as sets cannot.

Dictionaries are a specific case : key cannot be duplicates, but values can.


Lists Tuples Sets Dictionaries
Duplicate elements Duplicate elements No duplicate elements No duplicate elements

ToC

In [12]:
l1 = [1, 1, 2, 2]

print(l1)
[1, 1, 2, 2]
In [13]:
s1 = {1, 1, 2, 2}

print(s1)
{1, 2}
In [14]:
d1 = {'A': 1, 'A': 2, 'B': 1, 'C': 2}

print(d1)
{'A': 2, 'B': 1, 'C': 2}

Beginner

Recap of important collections attributes.


Lists Tuples Sets Dictionaries
Mutable Immutable Mutable Mutable
Ordered Ordered Unordered Unordered
Subscriptable / Slicable Subscriptable / Slicable Not subscriptable / Not slicable Subscriptable / Not slicable
Duplicate elements Duplicate elements No duplicate elements No duplicate elements

We can now get into more details with each collection.

ToC