liste = [1, 3.45, "bonjour", [4,5,6], ("a", "b")]
On évitera les tableaux avec des données de types différents, le traitement des données d'un tableau doit pouvoir se faire sans distinction de type.
0, 1, 2 . . . n − 1
Créer une liste vide :
L = []
L'ensemble des fonctionnalités liées à un objet liste
print dir(L)
Seules les fonctions : append, count, extend, index, insert, pop, remove, reverse, sort ont un intérêt pour nous. Comme d'habitude pour obtenir de l'aide sur une de ces fonctions on tape par exemple :
print(help(L.append))
Accéder aux éléments d'une liste, les éléments (encore mieux les objets) de la liste sont rangés par index croissant à partir de zéro.
\[ \begin{array}{|c|c|c|c|c|}\hline \text{index}& 0 & 1 & 2 & 3\\\hline \text{L} & 10.2 & 13 & 8 & 7.5\\\hline \end{array} \]
L = [10.2, 13, 8, 7.5]
L[2], L[3] # accès par rang
La taille d'une liste est obtenue à l'aide de la primitive python len qui appartient au module builtins
len(L)
Accéder au dernier élément de la liste
L[-1]
Ajouter un élément à la liste par extension
L.append(15)
print(L)
Accès aux éléments d'une liste de liste, dans l'exemple suivant nous avons deux sous listes dans une liste principale
tab = [[11,12,13],[7,9,11]]
\(\begin{array}{c|ccc} & 0 & 1 & 2 \\\hline 0 & 11 & 12 & 13 \\ 1 & 7 & 9 & 11\\ \end{array}\)
print "Premier élément de tab: tab[0] = ", tab[0], "\nÉlément de la ligne 0 et de la colonne 1 : tab[0][1] = ", tab[0][1]
print "Nombre de lignes : ", len(tab)
print "Nombre de colonnes:", len(tab[0])
Une autre façon de faire, les sous listes sont référencées par les variables K et L, ont peut donc modifier une sous liste directement à partir de K et L
K = ["H","He"]
L = ["Li", "Be", "B", "C", "N", "O", "F", "Ne"]
classif = [K, L]
print classif #On crée des sous listes (tableau à deux dimensions)
Concaténation de deux séquences
print K+L #On crée une seule liste
Un ensemble peut être défini en compréhension, c’est-à-dire qu'on le définit par une propriété caractéristique parmi les éléments d'un ensemble donné. Ainsi l'ensemble des entiers naturels pairs est clairement défini par compréhension, par la propriété « être pairs » parmi les entiers naturels.
\[\text{pair} = \{2\times n\, |\,n \in \mathbb{N}\}\]
De manière analogue en python, une liste peut-être définie par le filtrage du contenu d'une autre liste.
pair = [2*n for n in range(10)]
print(pair)
Considérons la éléments de la liste référencée par le nom de variable : pair
Nous pourrions maintenant ajouter \(+1\) à tous les éléments de pair avec les instructions suivantes:
for i in range (len(pair)):
pair[i] += 1
print(pair)
Dans ce cas nous perdons les valeurs pairs initialement dans la liste au profit des valeurs impairs.
Pour faire une copie de la liste : pair et modifier les valeurs de la copie afin conserver la liste pair intacte, beaucoup seront tentés dans un premier de temps par l'instruction impair = pair. Voyons ce que cela donne:
impair = pair
for i in range (len(impair)):
impair[i] += 1
print(impair)
Cela à l'air de fonctionner, mais que se passe-t-il pour la liste pair ?
print(pair)
La liste pair a elle aussi été modifiée.
Avec l'instruction impair = pair nous obtenons un simple alias vers la même liste, toute modifiaction de la liste référencée par b à pour effet de modifier la liste référencée par a
Tout dépend du contenu de la liste...
pair = [2*x for x in range(10)]
impair = pair[:] # copie superficielle [:]
for i in range (len(impair)):
impair[i] += 1
print(impair)
print(pair)
Le problème
pair = [[2*x] for x in range(10)]
impair = pair[:]
for i in range (len(impair)):
impair[i][0] += 1
print(impair)
print(pair)
Problème évident, à nouveau toute modification de impair modifie pair
La solution
Elle passe par l'import du package copy et l'utilisation de la fonction deepcopy
import copy
pair = [[2*x] for x in range(10)]
impair = copy.deepcopy(pair)
for i in range (len(impair)):
impair[i][0] += 1
print(impair)
print(pair)
shallow copy : copie des pointeurs des objets, dans les cas d'objets non mutables la copie superficielle permet de modifier les listes indépendamment l'une de l'autre. Attention les objets mutables seront modifiés dans les deux listes
Avec une copie superficielle
Toutes modifications d'objets non mutables dans une des listes n'entrainent pas de modification dans l'autre liste
Toutes modifications d'objets mutables entrainent des modifications dans les deux listes a et b
Avec une copie profonde
La copie profonde consiste dans une réelle copie des objets, les deux listes sont complètement indépendantes. Technique très couteuse en temps
#affichage graphique dans les cellules du notebook
%matplotlib inline
import time #un module pour la gestion du temps
import matplotlib.pyplot as plt #module graphique
tab=[]
start = time.time()
for i in range(10000000):#10 millions ou 7 zéros
tab.append(0)#ajout d'éléments avec la méthode append
print "%.3e" %(time.time()-start)
start = time.time()
tab = [0 for i in range(10000000)] #10 millions
print "%.3e" %(time.time()-start)
start = time.time()
tab = [0]*10000000 #10 millions
print "%.3e" %(time.time()-start)
Attention aux objets mutables, nous avons une liste de 10 millions de références vers le même objet
Considérons la liste réduite suivante :
L = [[0]]*5
print(L)
L[1][0] = 1
print(L)
Oups encore ce fichu objet mutable qui me cause du tourment !
tab = []
start = time.time()
for i in range(100000):# 5 zéros
tab = tab + [0]
print "%.3e" %(time.time()-start)
À éviter technique très coûteuse en temps
Avec des objets non mutables, on utilise la technique la plus rapide
Avec des objets mutables au choix entre la fonction append et la liste compréhension
On commence par initialiser une liste référencée par le nom de variable loto, à l'aide de valeurs entières aléatoires
from random import randint as rdint
loto = [rdint(0,49) for i in range(6)] # Attention randint[debut:fin] range[debut:fin[
On peut parourir la liste de deux façons :
for i in range(len(loto)): # renvoie la longueur du tableau
print loto[i]
for obj in loto:
print obj
for index, obj in enumerate(loto):
print "index",index, "valeur",obj
\[[\,[\,], [\,], [\,], \dots,[\,]\,]\]
nb_grille = 2
ticket=[]
#Remplissage d'un tableau à 2-dimensions
#chaque ligne une grille de loto (6 numéros)
for i in range(nb_grille):
grille=[]
for j in range(6):
grille.append(rdint(0,49))
ticket.append(grille)
#Même chose mais avec les listes compréhensions
#ticket = [[rdint(0,49)for i in range(6)]for j in range(nb_grille)]
#Le parcours démarre ici
#len(ticket) renvoie le nombre d'objets (grilles) contenus dans l'objet ticket
for i in range(len(ticket)):#parcours des lignes
print "Grille"+str(i)+": "
#len(ticket[i]) renvoie le nombre d'objets contenus dans une grille soit 6 numéros
for j in range(len(ticket[i])):#parcours des colonnes
print ticket[i][j]
liste = [rdint(0,100) for i in range(20)]
print liste
On parcours le tableau case par case jusqu'à ce qu'on trouve ou pas la valeur cherchée
Écrire la fonction recherche
def recherche(liste, k):
n = len(liste)#longueur de la liste
i = 0
#while liste[i] != k and i < n: # cette situation engendre une erreur
while (i < n) and (liste[i] != k): #l'opérateur AND est dit paresseux,
i += 1
if i < n:
return i, k
else:
return i,"pas trouve"
Pour tester notre fonction dans l'intervalle [0, 100] prenons la valeur 61
recherche(liste, 61)
Et avec une valeur qui n'est pas dans la liste ?
recherche(liste, 2)
Cette méthode s’applique à un tableau déjà trié et s’apparente à la technique diviser pour régner, elle impose donc un éventuel pré-traitement de tri
Prenons une autre valeur
def dicho(t, val):
g, d = 0, len(t)-1
compt = 0
while g <= d:
i = (g + d)/2
compt += 1
if t[i] == val:
return (i, compt, val)
if t[i] < val:
g = i + 1
else:
d = i - 1
return (None, compt)
tab = [3, 20, 87, 123, 128, 180, 200, 212]
dicho(tab, 127)
from random import randint as rd
tab = [rd(0,1000000) for i in range(10000000)]#10 millions de valeurs
tab.sort()
Comparaison expérimentale de la complexité en temps. Pour cela on utilise une liste x contenant 10 valeurs tirées au hasard dans l'intervalle [0, 1 000 000]
x = [rd(0, 1000000) for i in range(10)]
print x
start = time.time()
for elt in x:
print recherche(tab, elt)
print "Temps d'accès = %.3e s" %(time.time()-start)
start = time.time()
for elt in x:
print dicho(tab, elt)
print "Temps d'accès = %.3e s" %(time.time()-start)
Conclusion : La recherche par dichotomie est environ \(10^4\) fois plus rapide, complexité en \(\log(n)\)
Pourquoi les deux méthodes ne donnent-elles pas les mêmes indices de position pour une même valeur ?
Élément de réflexion avec la première valeur cherchée : 874047
tab[8738105 : 8738115]
import time
print time.strftime("Version du "+'%d/%m/%y %H:%M',time.localtime())
Christophe Casseau mail : isncaju@gmail.com