Open In Colab

Mickaël Tits CETIC [email protected]

Chapitre 2 - Les collections d'objets Python

Les collections de données sont des objets de type dit conteneur, qui contiennent d'autres objets, tels que des int, string, d'autres collections de données, ou tout autre type d'objet (tels que ceux vus dans les chapitres suivants).

Ces collections permettent de structurer les données, en représentant soit différentes données d'un même type (une liste de prix par exemple), ou les différentes caractéristiques d'un élément (e.g.: l'adresse, la surface, et le prix d'une maison en vente).

La représentation des objets sous forme de collection permet de les traiter de manière systématique: appliquer une instruction sur chaque élément, filtrer tous les éléments respectant une conditions, etc.

En Python, il existe différents types de collection de données, qui ont chacune leurs spécificités, avantages et inconvénients:

  • Les listes (list): une séquence ordonnée d'objets modifiable
  • Les tuples (tuple): une séquence ordonnée d'objets immuable (voir ci-dessous)
  • Les dictionnaires (dict): un ensemble de paires clé-valeur
  • Le string (str) peut être vu comme une séquence ordonnée de caractères
  • Le set (set): un ensemble d'objets distincts (pas abordés dans ce cours. Pour plus d'informations, voir ici ou ici)

Les listes

  • Séquence (collection ordonnée) de données
In [1]:
mystring = "1234"
mystring[2] = "0"
mystring
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-c156ff861b57> in <module>()
      1 mystring = "1234"
----> 2 mystring[2] = "0"
      3 mystring

TypeError: 'str' object does not support item assignment
In [2]:
#Pour déclarer une liste d'éléments, on utilise des crochets []
my_list = [1, 3, 5, 7]
my_list
Out[2]:
[1, 3, 5, 7]
In [3]:
# Une liste peut contenir différents types de données
my_list2 = [3.14, 42, my_list, True, 'Namur']
my_list2
Out[3]:
[3.14, 42, [1, 3, 5, 7], True, 'Namur']
In [4]:
#La fonction len() permet de connaître la longueur d'une liste
len(my_list)
Out[4]:
4

Accès aux données d'une liste

  • L'opérateur [x] permet d'accéder aux éléments d'une liste
  • Attention: l'indexation commence à 0
  • Une erreur est renvoyée si l'indice est invalide
  • Un indice négatif démarre de la fin de la liste
  • Le symbole ":" permet d'accéder à plusieurs éléments consécutifs
In [5]:
my_list[0]
Out[5]:
1
In [6]:
my_list[2]
Out[6]:
5
In [0]:
#Une erreur est obtenue si l'indice est invalide
#my_list[4]
In [8]:
#Un indice négatif part de la fin d'une liste
my_list[-1], my_list[-2]
Out[8]:
(7, 5)
In [9]:
#On peut accéder à plusieurs éléments d'une liste en utilisant un colon (:)
print(my_list[1:3]) #permet d'accéder aux éléments entre les indices 1 et 3 (3 exclu)
print(my_list[1:]) #permet d'accéder aux élements à partir de l'indice 1 (jusqu'au dernier)
print(my_list[:2]) #permet d'accéder aux éléments du premier jusqu'à l'indice 2 (2 exclu)
print(my_list[:]) #permet d'accéder à tous les éléments
print(my_list[1:4:2]) #permet d'accéder aux éléments de 1 à 4 (4 exclu) par pas de 2
[3, 5]
[3, 5, 7]
[1, 3]
[1, 3, 5, 7]
[3, 7]

Manipulation de listes

  • Addition (+) = concaténation: [1,2] + [3,4] = [1,2,3,4]
  • Multiplication (*) = multiple concaténation: [1,2]*2 = [1,2,1,2]
  • Opération élémentaire => compréhension de liste: [i*4 for i in my_list]
In [10]:
#Additionner des listes résulte en une concaténation
my_list+[4, 5]
Out[10]:
[1, 3, 5, 7, 4, 5]
In [11]:
#Naturellement, la multiplication résulte en une multiple concaténation
my_list*2, [0]*10
Out[11]:
([1, 3, 5, 7, 1, 3, 5, 7], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
In [12]:
#On peut modifier les éléments d'une liste (on dit que c'est un objet mutable)
my_list[0] = 42
my_list
Out[12]:
[42, 3, 5, 7]
In [0]:
#On ne peut pas additionner un entier à une liste
#my_list + 4
In [14]:
#Pour réaliser une opération sur chaque élément d'une liste, on peut utiliser une boucle for

#%%timeit

my_list_plus_4 = [0]*len(my_list)

for i in range(len(my_list)):
  my_list_plus_4[i] = my_list[i] + 4

my_list_plus_4
Out[14]:
[46, 7, 9, 11]
In [15]:
#Ou

#%%timeit

my_list_plus_4 = [] #liste vide

for item in my_list:
  my_list_plus_4.append(item + 4) #ajouter un élément à la nouvelle liste
  
my_list_plus_4  
Out[15]:
[46, 7, 9, 11]

Fonctions de base sur les listes

Pour chaque type de collection d'objets, différentes fonctions de base permettent de réaliser différents type d'opérations, tels que:

In [16]:
a = any([False,False,True]) #output: True
A = all([False,False,True]) #output: False
print(l, s, M, m, a, A)
4 11.2 4.2 1 True False

Compréhension de listes

Instruction plus compacte, et plus rapide (et souvent plus lisible) pour réaliser une opération sur chaque élément dans une liste.

Syntaxe:

new_list = [instruction for item in list if condition]

Code équivalent sans compréhension de liste:

new_list = []
for item in list:
  if condition:
    output = instruction
    new_list.append(output)
In [17]:
#Pour additionner un scalaire à chaque éléments d'une list, on peut aussi utiliser une "list-comprehension": plus compacte, et plus rapide

#%%timeit

my_list_plus_4 = [i + 4 for i in my_list]
my_list_plus_4
Out[17]:
[46, 7, 9, 11]

On peut représenter une matrice par une liste de listes. On peut réaliser une opération entre deux liste avec une double compréhension de liste.

Remarque: En pratique, pour les opérations mathématiques on utilise généralement des librairies spécialisées, telles que numpy (voir Chapitre 5).

In [18]:
my_list1 = [1,2,3,4]
my_list2 = [5,6,7,8]

#my_list1'*my_list2:
matprod = [ [a*b for b in my_list2] for a in my_list1]
matprod
Out[18]:
[[5, 6, 7, 8], [10, 12, 14, 16], [15, 18, 21, 24], [20, 24, 28, 32]]
In [19]:
my_list = [-1, 2, 3, -7, 5, -4, 2, 8, -56, 9, 5.3, -4.5]

#Remplacer les nombres négatifs par 0 ?
zero_list = []
for item in my_list:
  if item < 0:
    zero_list.append(0)
  else:
    zero_list.append(item)

print('négatifs remplacés par 0:', zero_list)

#Ou
zero_list = [0 if item < 0 else item for item in my_list]

print('négatifs remplacés par 0, bis:', zero_list)
négatifs remplacés par 0: [0, 2, 3, 0, 5, 0, 2, 8, 0, 9, 5.3, 0]
négatifs remplacés par 0, bis: [0, 2, 3, 0, 5, 0, 2, 8, 0, 9, 5.3, 0]
In [20]:
#Eliminer les nombres négatifs d'une liste ?
my_list = [-1, 2, 3, -7, 5, -4, 2, 8, -56, 9, 5.3, -4.5]
positive_list = [item for item in my_list if item >= 0]
print('négatifs éliminés:', positive_list)
négatifs éliminés: [2, 3, 5, 2, 8, 9, 5.3]

Exercice: filtrer une liste de prix

Parmi une liste de prix, ne garder que ceux entre 200.000 et 300.000.

In [0]:
prices = [50000, 1000, 500000, 200000, 300000, 400000, 260000, 270000]
In [0]:
 

Solutions des exercices ici

Les tuples

  • Les tuples sont des listes immuables.
  • A quoi ça sert ?
    • Traitement plus rapide dans certains contextes
    • Moins de risque de confusion due à de multiple références d'un objet mutable (voir Chapitre 3)
    • Certains traitements nécessitent des objets immuables et ne peuvent donc pas être utilisés avec des listes. E.g.: les clés d'un dictionnaires (voir Section suivante)

Remarque: on utilise en général des tuples pour représenter une structure de données:

E.g.: a_house = ("Rue de Fer 25, Namur", "villa", 2000000, 4, "immoweb")

Avec des types généralement différents. Ce type de représentation a peu d'intérêt d'être mutable. Il représente les caractéristiques d'un bien immobilier dans cet exemple (addresse, type, prix, nombre de chambre, source). A l'inverse, on utilise généralement une liste pour représenter une collection d'objets homogènes (bien que ça accepte des types hétérogènes).

E.g.: house_prices = [200000, 350000, 170000, 270000]

L'utilisation d'une liste permet de facilement ajouter/supprimer des éléments (généralement du même type, e.g. de nouveaux prix), et de réaliser rapidement des instructions sur chaque élément grâce aux compréhensions de liste.

In [22]:
#L'utilisation de parenthèses permet de déclarer un tuple
my_tuple = ("Rue de Fer 25, Namur", "villa", 2000000, 4, "immoweb")
my_tuple
Out[22]:
('Rue de Fer 25, Namur', 'villa', 2000000, 4, 'immoweb')
In [23]:
my_set = {2,1,1,2,1,3,1}
my_set.add(4)
my_set
Out[23]:
{1, 2, 3, 4}
In [24]:
# Les parenthèses sont conventionnelles mais ne sont cependant pas nécessaires pour déclarer un tuple
my_tuple = "Rue de Fer 25, Namur", "villa", 2000000, 4, "immoweb"
my_tuple
Out[24]:
('Rue de Fer 25, Namur', 'villa', 2000000, 4, 'immoweb')
In [25]:
my_tuple[0]
Out[25]:
'Rue de Fer 25, Namur'
In [26]:
#On ne peut pas modifier les éléments d'un tuple, on dit que c'est un "objet immuable" (immutable en anglais). La ligne suivante donne donc une erreur
my_tuple[2] = 300000
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-26-86038907a6b7> in <module>()
----> 1 my_tuple[2] = 300000

TypeError: 'tuple' object does not support item assignment
In [28]:
my_list = [-1, 2, 3, -7, 5, -4, 2, 8, -56, 9, 5.3, -4.5]
my_tuple = tuple(my_list)
my_tuple
Out[28]:
(-1, 2, 3, -7, 5, -4, 2, 8, -56, 9, 5.3, -4.5)

Remarque:

Les commandes commençant pas % ou %% sont des "commandes magiques" propres aux Notebooks, et ne fonctionnent pas dans un script python classique.

Les dictionnaires

Les dictionnaires sont des collection de données de type paire clé-valeur. On utilise des accolades {} pour déclarer un dictionnaire. Les dictionnaires sont des objets mutables, ce qui veut dire qu'on peut ajouter des clés et des valeurs après déclaration. Les dictionnaires permettent notamment de manipuler un ensemble de données tabulaires.

Remarque: Nous verrons plus loin qu'ils existe une représentation plus pratique de données tabulaires: le DataFrame.

In [29]:
house1 = {"address":"Rue de Fer 25, 5000 Namur", "website":"immoweb", "price": 400000, "surface":150}

house2 = {"address":"Rue de Bruxelles 42, 5000 Namur", "website":"immoweb", "price": 350000, "surface":200}

house3 = {"address":"Porte de Namur 25, Bruxelles", "website":"immovlan", "price": 400000, "surface":100}

#On peut modifier une valeur
house1["surface"] = 170

#On peut aussi ajouter une clé
house1["rooms"] = 4

#On peut itérer sur les clés d'un dictionnaire
for key in house1:
  print(key, ' - ', house1[key])
  
#On peut utiliser des compréhension de dictionnaire (ce qui crée un nouveau dictionnaire)
house1_string = {key:str(house1[key]) for key in house1}
print("Valeurs du dictionnaire converties en strings:", house1_string)

#On peut aussi utiliser des compréhensions de liste (Remarque: on peut utiliser des compréhension de liste sur tout objet itérable, c'est-à-dire utilisable avec l'instruction "for")
is_string = [type(house1[key]) is str for key in house1]
print("is_string:", is_string)
address  -  Rue de Fer 25, 5000 Namur
website  -  immoweb
price  -  400000
surface  -  170
rooms  -  4
Valeurs du dictionnaire converties en strings: {'address': 'Rue de Fer 25, 5000 Namur', 'website': 'immoweb', 'price': '400000', 'surface': '170', 'rooms': '4'}
is_string: [True, True, False, False, False]
In [30]:
#Dictionnaire de dictionnaires
house_dataset = {1:house1,2:house2,3:house3}


# => Facile d'extraire et ajouter une maison:
my_house = house_dataset[2]
house_dataset[4] = {"address":"Rue de Fer 26, Namur", "website":"immovlan", "price": 370000, "surface":170}


# Facile de filtrer les maisons:
big_houses = {k:house_dataset[k] for k in house_dataset if house_dataset[k]["surface"] > 150}
print("Grandes maisons:", big_houses)


# Moins facile d'extraire ou ajouter une caractéristique pour toutes les maisons: on doit modifier itérativement chaque élément (maison)!
rooms = [4,3,4,2]
keys = list(house_dataset.keys())
for i in range(4):
  house_dataset[keys[i]]["rooms"] = rooms[i]

print(house_dataset)
Grandes maisons: {1: {'address': 'Rue de Fer 25, 5000 Namur', 'website': 'immoweb', 'price': 400000, 'surface': 170, 'rooms': 4}, 2: {'address': 'Rue de Bruxelles 42, 5000 Namur', 'website': 'immoweb', 'price': 350000, 'surface': 200}, 4: {'address': 'Rue de Fer 26, Namur', 'website': 'immovlan', 'price': 370000, 'surface': 170}}
{1: {'address': 'Rue de Fer 25, 5000 Namur', 'website': 'immoweb', 'price': 400000, 'surface': 170, 'rooms': 4}, 2: {'address': 'Rue de Bruxelles 42, 5000 Namur', 'website': 'immoweb', 'price': 350000, 'surface': 200, 'rooms': 3}, 3: {'address': 'Porte de Namur 25, Bruxelles', 'website': 'immovlan', 'price': 400000, 'surface': 100, 'rooms': 4}, 4: {'address': 'Rue de Fer 26, Namur', 'website': 'immovlan', 'price': 370000, 'surface': 170, 'rooms': 2}}
In [31]:
#Dictionnaire de listes ?
#Remarque: on peut écrire la déclaration d'une collection d'objet (list [], tuple () ou dict{}) sur plusieurs lignes.
house_dataset2 = {"address":["Rue de Fer 25, 5000 Namur", "Rue de Bruxelles 42, 5000 Namur", "Porte de Namur 25, Bruxelles"],
          "website":["immoweb","immoweb","immovlan"],
           "price": [400000, 350000, 400000],
           "surface":[150, 200, 100]}


# Plus facile d'extaire et ajouter une caractéristique:
prices = house_dataset2["price"]
house_dataset2["rooms"] = [4,3,4]


#Facile de filtrer des caractéristiques:
house_numbers = {k:house_dataset2[k] for k in house_dataset2 if type(house_dataset2[k][0]) is int}
print("Variables numériques:", house_numbers)


# Mais moins facile d'extraire ou ajouter une maison: on doit modifier itérativement chaque élément (liste)!
house4 = ("Rue de Fer 26, Namur", "immovlan", 370000, 170, 2)
keys = list(house_dataset2.keys())
for i in range(5):
  house_dataset2[keys[i]].append(house4[i])

print(house_dataset2)
Variables numériques: {'price': [400000, 350000, 400000], 'surface': [150, 200, 100], 'rooms': [4, 3, 4]}
{'address': ['Rue de Fer 25, 5000 Namur', 'Rue de Bruxelles 42, 5000 Namur', 'Porte de Namur 25, Bruxelles', 'Rue de Fer 26, Namur'], 'website': ['immoweb', 'immoweb', 'immovlan', 'immovlan'], 'price': [400000, 350000, 400000, 370000], 'surface': [150, 200, 100, 170], 'rooms': [4, 3, 4, 2]}

Remarque:

On verra plus tard qu'il existe une représentation plus pratique des données (le DataFrame), qu'on peut utiliser avec des librairies spécialisées.

Les strings sont des collections de données du même type

  • le string est une séquence (une collection ordonnée) de caractères
  • il existe de nombreuses méthodes spécifiques aux string
In [32]:
address = "Rue de Bruxelles"

print("Premier élément:\n", address[0])

#On peut convertir un string en une autre collection simple (e.g., une liste)
print("Converti en liste:\n", list(address))

print("Converti en minuscules:\n", address.lower() )

print("Split des mots (i.e. splitter les chaînes entre les whitespaces (' ') ):\n", address.split(' ') )
Premier élément:
 R
Converti en liste:
 ['R', 'u', 'e', ' ', 'd', 'e', ' ', 'B', 'r', 'u', 'x', 'e', 'l', 'l', 'e', 's']
Converti en minuscules:
 rue de bruxelles
Split des mots (i.e. splitter les chaînes entre les whitespaces (' ') ):
 ['Rue', 'de', 'Bruxelles']

Un opérateur spécifique aux collections: "in"

  • for i in [0,1,2,3]: itération sur chaque élément de la collection
  • if "n" in ["n","a","m","u","r"]: permet de vérifier la condition qu'un élément soit dans une collection
  • ça fonctionne aussi pour les strings: "n" in "namur" => True
In [33]:
address_list = ['Rue de la Loi, Bruxelles','Rue de Bruxelles 61, Namur',
                'Rue de Fer 42, Namur', 'Chaussée de Bruxelles',
                'Gauffre de Bruxelles', 'Grand Place, Bruxelles']

brux = []

for address in address_list:
  if 'Bruxelles' in address:
    brux.append(address)

print(brux)
#Ou
brux = [address for address in address_list if 'Bruxelles' in address]

print(brux)
['Rue de la Loi, Bruxelles', 'Rue de Bruxelles 61, Namur', 'Chaussée de Bruxelles', 'Gauffre de Bruxelles', 'Grand Place, Bruxelles']
['Rue de la Loi, Bruxelles', 'Rue de Bruxelles 61, Namur', 'Chaussée de Bruxelles', 'Gauffre de Bruxelles', 'Grand Place, Bruxelles']

Récapitulatif de syntaxe

Quand utiliser (), [] et {}

Pour assigner une collection d'objet, on utilise [] pour une list, () pour un tuple, {} pour un dict. Pour accéder aux éléments d'une collection, on utilise toujours des crochets [i]. E.g.: my_list[i], my_tuple[i], my_dict[i] Pour appeler une function ou une méthode, on utilise des parenthèses. E.g. print(my_list), ou my_list.append(42)

In [34]:
my_list = ['a','b','c']
my_tuple = (1,2,3)
my_dict = {'a':1, 'b':2, 'c':3}

my_list.append('d')
print(my_list[3], my_tuple[0], my_dict['c'])
d 1 3

Nouveaux mots-clés et types de données

In [0]:
list
dict
tuple
set

len
sum
max
min
any
all

in

#list comprehension syntax:
[instruction for item in list if condition]

#dictionary comprehension syntax
{key_instruction:value_instruction for key in dict if condition}

Vous êtes maintenant capables de manipuler des collections d'objets, vous pouvez passer au Chapitre 3: Concepts avancés de programmation: exceptions, fonctions, objets