Chapter 3_Collections


I. Python Collections

Python collections are built-in container data types used to store multiple values in a single variable.

Main collection types:

  • list → mutable, ordered
  • tuple → immutable, ordered
  • set → mutable, unordered, unique elements
  • dict → mutable, ordered (Python 3.7+)






II. Lists (Mutable, Ordered, Iterable)

1. Creation:

list1 = [ ]
list2 = list()

2. Access:

2.1 Length:

len(list1)

2.2 By Index:

list1[i]

2.3 By slicing:

list1[i:j]

2.4 Membership:

val in list1

not in also exists


2.5 Looping:

for i in list1 # method 1

for i in range(len(list1)) # method 2

while ( i < len(list1) ): # method 3

3. Modification:

3.1 Change Items:

3.1.1 Change item value:

list1[i] = new_val

3.1.2 Change range of items values:

list1[i:j] = list2
# ⚠️ If len(list2) is different from (j - i), the list size will change accordingly.

# Example:
list1 = ['a', 'b', 'c','d']
list1[0:2] = [0]
print(list1) # [0, 'c', 'd']

3.2 Add Items:

3.2.1 Insert an item to the end

list1.append(val) 

3.2.2 Insert an item at a specific index

list1.insert(index, val)

3.2.3 Append elements from another iterable ( list, tuple, dict, … )

list1.extend(iterable1)
# If iterable1 is a dict, only keys are added.

3.3 Remove Items:

3.3.1 Remove an item from the end:

list1.pop()

3.3.2 Remove val first occurrence:

list1.remove(val)

3.3.3 Remove an item from a specific index:

list1.pop(index)
del list1[index]

3.3.4 Remove the entire list:

del list1

3.3.5 Clear list items:

list1.clear()

4. List Comprehension:

List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list.

newlist = [expression for item in iterable if condition]
# the `if` part is optional
# which is useful for filtering the iterable items.
# you can also add a condition as an expression to control the outcome
#Examples:
newlist = [x for x in range(10)]
newlist = [x for x in range(10) if x < 5]
newlist = [x if x != "banana" else "orange" for x in fruits] # Expression here is a shorthand if-else

5. Sort Lists:

5.1 .sort([reverse = True], [key = keyFunc])

# key func is a function that returns a number/thing that will be used to sort the list
# Example:
list1.sort(key = str.lower) # case insensitive sort.

5.2 Reverse order:

list1.reverse()

6. Copy Lists:

6.1 .copy():

list2 = list1.copy()

6.2 list():

list2 = list(list1)

6.3 Using slicing:

list2 = list1[:]

7. Join Lists:

7.1 + Operator:

list3 = list1 + list2

7.2 .extend():

list1.extend(list2)

7.3 for loop + append():

for x in list2:
    list1.append(x)

8. Other methods:

list1.index( val ) # First occurrence index
list1.count( val ) 






III. Tuples (Immutable, Ordered, Iterable)

Note: Tuples items are unchangeable but there is a way around it : tuple ->(convert) list -> modify list -> tuple

Tuples are immutable but can contain mutable objects.

1. Creation:


tuple1 = ('a', 'b', 'c') # multiple items
tuple2 = ('a', ) # Single item
tuple3 = ('a') # ❌ False not a tuple, type = str

2. Access:

2.1 Length:

len(tuple1)

2.2 By Index:

tuple1[i]

2.3 By slicing:

tuple1[i:j]

2.4 Membership:

val in tuple1

not in also exists


2.5 Looping:

for i in tuple1 # method 1

for i in range(len(tuple1)) # method 2

while ( i < len(tuple1) ): # method 3

3. Modification:

3.1 Change item value

x = ('a', 'b')
l1 = list(x)
l1[0] = 0
x = tuple(l1)

3.2 Add items:

x = ('a','b')
l1 = list(x)
l1.append('c')
x = tuple(l1)

3.3 Add tuple to an existing tuple

x = ('a', 'b')
y = ('c', 'd')

z = x + y # creates another object

3.4 Remove items:

x = ('a', 'b')
l1 = list(x)
l1.remove('a')
x = tuple(l1)

3.5 Delete tuple:

del thisTuple

4. Unpacking

Using Asterisk *

a, *b, c = [1, 2, 3, 4]
print(a, b, c)  # 1 [2, 3] 4

5. Join, multiply tuples

tuple3 = tuple1 + tuple2 # join tuples

tuple4 = tuple1 * 3 # repeat items

6. Other methods:

tuple1.count(val)

tuple1.index(val)






IV. Sets (Mutable, Unordered, Iterable)

Note Sets items should be immutable tuples containing mutable objects are NOT allowed inside sets.

# 1 is considered as True, same thing for 0 for sets
{1, True}  # only one element
{0, False} # only one element

1. Creation:

set1 = {'a', 'b', 'c'}
set2 = set(('a', 'b', 'c'))

2. Access:

Note set items can’t be accessed by index/value nor slicing. but it can be accessed using a for loop

2.1 For loop:

# this is the only way to access and to loop through a set.
for x in set1:
    print(x)

2.2 Membership:

val in set1

not in also exists


3. Modification:

3.1 add item:

set1.add(val)

3.2 add items from another iterable:

set1.update( iterable1 )

3.3 Remove item:

set1.remove(val) # if the val doesn't exist it raises an error
set1.discard(val) # safe option for .remove()
set1.pop() # remove a random val because sets are unordered.

3.4 Clear set:

set1.clear() 

4. Join Sets:

4.1 UNION:

set3 = set1.union(set2)
set3 = set1 | set2

set1.update( set2 ) # modifty set1

4.2 INTERSECT:

set3 = set1.intersection(set2)
set3 = set1 & set2

set1.intersection_update(set2)

4.3 DIFFERENCE:

set3 = set1.difference(set2)
set3 = set1 - set2

set1.difference_update(set2)

# Symeteric difference: keep elements, that are not present in both sets
set3 = set1.symmetric_difference(set2)

set1.symmetric_difference_update(set2)

5. FrozenSet (Immutable version of sets)

5.1 Creation:

x = frozenset({'a', 'b'})

5.2 Methods:

# return NEW frozensets
x.copy() # returns the same object because frozenSets are immutable.
x.union(other)
x.intersection(other)
x.difference(other)
x.symmetric_difference(other)

# return booleans 
x.issubset(other)
x.issuperset(other)
x.isdisjoint(other)






V. Dicts (Mutable, Ordered, Iterable)

1. Creation:

Note

  1. dict keys : immutables
  2. Dictionaries preserve insertion order since Python 3.7+
dict1 = {k0:v0, k1:v1, ...}
dict2 = dict( k0 = v0, k1 = v1)

2. Access:

2.1 Length:

len(dict1)

2.2 By key:

dict1[key1]

2.3 By .get():

dict1.get(key1)
dict1.get(key1, default)     # Custom default value

2.4 get keys:

keys = dict1.keys() # returns a dict_keys view (iterable, not a list)

2.5 get values:

values = dict1.values() # returns a dict_values view

2.6 get elements as list of tuples: [(k0,v0), (k1,v1), ... ] (as a view, not a list)

dict1.items()

Note: Views are updated automaticaly

2.7 Membership:

key1 in dict1 # checks keys only, not values

not in also exists


2.8 Looping:

for k,v in dict1.items() # method 1

for k in dict1.keys() # method 2 
# or 
for k in dict1 # method 2'


for v in dict1.values() #method 3

3. Modification:

3.1 Change/add items:

dict1[ key1 ] = val
dict1.update({key1 : val}) # Can update multiple keys at once

# For both methods:
# Add the item if key1 does not exist.
# Update the value if key1 already exists

3.2 Remove items:

dict1.pop( key1 ) # Remove item by key

dict1.popitem() # removes the last inserted item 

del dict1[ key1 ]

del dict1 # Delete entire dict

dict1.clear()

4. Copy a Dictionary:

dict2 = dict1.copy()

dict2 = dict(dict1)

Note

there is also what so called nested dictionaries

example: { “user1”:{ “name”:“achraf”, “lastName”:“SL” }, … }

dict1[“user1”][“name”]

5. Dictionary comprehension:

{key_expr: value_expr for item in iterable [if condition]}

6. Dictionary Methods:

dict1 = dict.fromkeys(keys, only_one_value) # Returns a dictionary with the specified keys and value 
# if `only_one_value` is not specified it becomes None
value =  dict1.setdefault(key, val) # Returns the value of the specified key. If the key does not exist: insert the key, with the specified value and return it.

Note dict.fromkeys() if only_one_value is mutable

d = dict.fromkeys(['a', 'b'], [])
d['a'].append(1)
print(d)  # {'a': [1], 'b': [1]}