6.1 — Tuples, Immutable objects, Sets

Tuples

  • A tuple is an ordered collection of objects, like lists.
  • A tuple is immutable. A tuple object cannot be modified after it is created.
  • We create a tuple using parentheses ().
1tup = (1, 2, 3)
2print(type(tup)) # <class 'tuple'>
3
4# tuple with only one item
5tup = (10,) # comma is required!
6print(tup, type(tup)) # (10,) <class 'tuple'>

We can use tuple function to convert other sequences such as lists and strings into a tuple.

1word = "apple"
2tup = tuple(word)
3print(tup)
4# ('a', 'p', 'p', 'l', 'e')
5
6primes = [2, 3, 5, 7]
7primes = tuple(primes)
8print(primes)
9# (2, 3, 5, 7)

Tuples are immutable

Items cannot be added, removed or changed in a tuple.

Therefore, unlike lists, none of the operations that modify a tuple are allowed.

1x = (1, 1, 2, 3, 5)
2x[3] = 100
3# TypeError: 'tuple' object does not support item assignment

Assigning a new object to a variable does not affect/modify the current object the variable refers to.

1x = (1, 1, 2, 3, 5)
2
3# This does not modify the above tuple object so
4# it is allowed
5x = (1, 2, 3)

In general, operations that do not modify a tuple are available.

1tup = (45, 23, 'abc')
2
3# Indexing and slicing work the same way as lists.
4print(tup[1])
5# 23
6print(tup[1:])
7# (23, 'abc')
8
9print(len(tup)) # number of items in a tuple
10# 3

Similary min(), max(), and sum() functions work with tuples.

1tup = (45, 23, 'abc')
2
3# Following methods are available for tuples
4print(tup.index("abc"))
5# 2
6
7print(tup.count(23))
8# 1

Since a tuple is a sequence, we can use it in a for loop just like a list:

1numbers = (10, 20, 30)
2
3for n in numbers:
4 print(n)
Output
10
20
30

Why use tuples?

If lists are more flexible than tuples, why should we use tuples?

  • Immutability is useful to avoid changing data by mistake.
  • We can use tuples as elements of sets and as keys in a dictionary.
  • Programs are a bit faster when working with tuples.

Try the problem “Euclidean distance using list/tuple” on Ed Lessons.

Object Identity

  • Each object is assigned an ID at its creation (think of a memory address).
  • This ID is unique and constant for this object as long as the object exists.
  • The built-in function id() can be used to retrieve the ID of an object.
1x = 1234
2y = x
3# x and y both refer to the same object,
4# therefore the IDs are the same.
5print(id(x) == id(y)) # True

1# x and y point to two different objects,
2# therefore we expect x and y to have different IDs.
3x = 1234
4y = 5678
5print(id(x) == id(y)) # False
6
7# x and y point to two different objects (with the same value),
8# therefore we expect x and y to have different IDs.
9x = int("1234") # integer 1234
10y = int("12" + "34") # integer 1234
11
12print(id(x) == id(y)) # False

Identity operators

  • is and is not are comparison operators used to check if the two operands refer to the same object.
  • Using is operator means: are two variables referring to one and the same object?
  • Using == operator means: are two variables referring to objects that contain same data?
1x = int("1234")
2y = int("12" + "34")
3z = x
4print(x == y) # True
5print(x == z) # True
1x = int("1234")
2y = int("12" + "34")
3z = x
4print(x is y) # False
5print(x is z) # True

Mutable vs Immutable objects

Immutable: the content of the object cannot be changed after the object has been created.

  • e.g. str, int, float, tuple

Mutable: the content of the object can be changed after its creation without changing its identity.

  • e.g. list, dict, set

Strings are immutable

Unlike lists, we cannot use the square brackets to modify a character in the string.

s = "cats"
s[0] = "r" # TypeError: 'str' object does not support item assignment

All strings operations that seem to change a string actually create a new string.

1s = "cat"
2t = s
3print("Before:", s is t)
4
5s = s.replace('c','r')
6print("After:", s is t)
7
8print("s:", s)
9print("t:", t)
Output
Before: True
After: False
s: rat
t: cat

Lists are mutable

The following code does not create a copy of the list x.
It simply create a new variable name for the same list.

1x = [1, 2, 3]
2y = x # new name y for same list
3
4print(x is y) # True

Let us see some implications of this.

In the following illustrations, think about what is modified.

  • Whether a variable changes its value i.e. the variable refers to a different value
  • Whether a list object is modified i.e. some element of the list is changed.

Sets

  • A set is a an unordered collection of immutable objects.
  • A set always contains unique elements, unlike lists and tuples which allow duplicates.
  • A set is unordered i.e. we cannot use indexing or slicing on a set object
numbers = {1, 2, 3}
print(numbers) # {1, 2, 3}
print(type(numbers)) # <class 'set'>
# only unique values are kept
numbers = {1, 2, 3, 1, 3}
print(numbers) # {1, 2, 3}
print(len(numbers)) # 3

Other ways to create a set

1# a set can be created from any sequence
2# such as list, tuple or a string
3things = set([10, 42, "apple", 42])
4print(things) # {'apple', 10, 42}
5
6word = "pineapple"
7letters = set(word)
8print(letters)
9# {'p', 'n', 'l', 'i', 'e', 'a'}

1# create an empty set
2empty_set = set()
3print(len(empty_set)) # 0
4
5# This does not create an empty set!
6empty_dictionary = {}
7print(type(empty_dictionary)) # <class 'dict'>

Set elements must be immutable

A set can contain int, float, str, bool and tuple objects because they are all immutable.

But a set cannot contain a list because lists are mutable.

1# tuples are immutable so allowed in set
2points = {(1, 1), (3, 10), (3, 10)}
3print(points)
4# {(3, 10), (1, 1)}
5
6# lists are mutable so not allowed
7points = {[1, 1], [3, 10], [3, 10]}
8# TypeError: unhashable type: 'list'

Set operators and methods

1# set.add(x):
2# Adds an element x to the set if x does not exist in the set.
3# Does not return anything.
4
5numbers = {1, 2, 3}
6numbers.add(20)
7print(numbers) # {1, 2, 3, 20}
8
9numbers.add(3)
10print(numbers) # {1, 2, 3, 20}

1# set.remove(x):
2# Remove an element x from the set. Does not return anything.
3# Throws KeyError if element x is not present in the set.
4
5numbers = {1, 2, 3}
6numbers.remove(2)
7print(numbers) # {1, 3}
8
9numbers.remove(5) # KeyError: 5

in, not in operators can be used to check if an element exists in a set.

1numbers = {1, 2, 3}
2print(2 in numbers) # True
3print(5 not in numbers) # True
4
5shapes = {'circle', 'square'}
6print("circle" in shapes) # True

Sets cannot be indexed or sliced because they are not ordered.

1primes = {2, 3, 5, 7, 11}
2primes[4] # TypeError: 'set' object is not subscriptable

But we can use for loop to iterate over the items:

1# The order in which items will be printed
2# is not defined because sets are not ordered
3
4numbers = {10, 1, 5, 20}
5for n in numbers:
6 print(n)
Output
1
10
20
5

Why use sets?

  • Set are faster than lists and tuples, when inserting, removing and searching elements.
  • When order of elements is important or when elements are mutable, use lists or tuples
  • When only unique immutable elements need to be stored, use sets.

Try the problem “Word count” on Ed Lessons.

Try the problem “List duplicates” on Ed Lessons.