Listes en Python La structure de données la plus utilisée en Python

publicité
Listes en Python
La structure de données la plus utilisée en Python est la liste. Les listes python sont en fait des tableaux dynamiques
: on peut accéder à n’importe quel élément du tableau et le modi…er en temps constant et on peut au besoin ajouter
des éléments en queue de tableau, dans le cas le pire en temps proportionnel à la taille du tableau et en temps amorti
constant.
J Dé…nitions d’une liste
Considérons l’exemple dé…ni par
a = [1,2,3,"toto"]
J Longueur de la liste : len(a) renvoie la longueur n de la liste (c’est-à-dire le nombre d’éléments)
J Obtention et modi…cation d’un élément : a[i] renvoie l’élément d’indice i dans la liste, pour 0
i < n:
Ainsi, avec l’exemple précédent, a[0] renvoie 1 et a[3] renvoie ’toto’
On peut modi…er les éléments d’une liste : a[1] = a[1]+1
Important : Ces opérations se font en temps constant (en moyenne, et dans le cas le pire en temps proportionnel à n).
J Listes dé…nies en extension (construites à l’aide d’une itérateur sur une autre liste) :
La syntaxe élémentaire générale est : [ f(i) for i in range(n)].
Exemples :
[3*i**2 + 1 for i in range(1,10)] renvoie la liste [0,1,2,3,4,5,6,7,8,9]
[i**2 for i in range(10) if i%3 != 1] renvoie la liste [0,2,3,5,6,8,9]
[(3*i+j) for i in range(2) for j in range(3)] : Les éléments sont placés dans l’ordre lexicographique associé à
(i; j) : le plus simple est de se souvenir que cette construction remplace deux boucles for imbriquées dans le même ordre.
Ainsi, on obtient la liste [0,1,2,3,4,5].
J Appartenance à une liste (utilisée souvent dans les tests) : (x in a) vaut True x appartient à l.
J Opérations sur les listes
Concaténation de listes : a + [4,5] renvoie [1,2,3,’toto’,4,5]
Répétitions : a * 3 équivaut à a + a + a
Important : On peut ainsi dé…nir et initialiser à 0 un tableau a de longueur n par : a = [0]*n
Une autre solution est a = [0 for i in range(n)].
J Sous-listes (“slicing”)
a[2:4] renvoie [3,"toto"] ; en particulier, a[0:len(a)] renvoie la liste complète
De manière générale, la syntaxe des tranches est a[start:stop:step] où start désigne l’indice où commencer, stop
l’indice avant lequel il faut s’arrêter et step l’incrément à donner à l’indice à chaque étape. On peut omettre ces valeurs
start, stop, step, auquel cas Python leur donne respectivement les valeurs 0, n et 1, où n est la longueur de la liste.
Ainsi a[1::2] désigne la liste des éléments de a de rang impair.
J Opérations orientées-objets sur les listes (cf aussi dernier paragraphe)
Pour les opérations sur les listes, il vaut mieux privilégier les procédures orientées-objet :
- Ajout d’un élément x à la …n de la liste a : a.append(x)
- Suppression et a¤ectation d’un élément : a.pop(i) supprime le i-ième élément et renvoie sa valeur.
La valeur par défaut de i est le dernier indice : a.pop() supprime le dernier élément de la liste.
- Concaténation de deux listes (modi…e la liste a) : a.extend(b)
- Insertion d’un élément x en position i : a.insert(i,x)
- Tri (rapide) de la liste (d’enteirs ou de ‡ottants) : a.sort()
- Inversion de la liste : ; a.inverse()
- Suppression de l’élément en position i : del a[i] et plus généralement del a[i:j]
- Suppression de la première occurrence de x : a.remove(x)
Remarque : Les procédures tri, extend, insert, del et remove sont de type None. Exemple :
a = [4,1,3] ; a.sort() ; a.append(2) ; print(a)
# a¢ che [1; 3; 4; 2]
J Copie
b = list(a) revient à faire une copie (physique) du tableau a:
Remarque : Autre méthode possible : b = [a[i] for i in range(len(a))]
Listes et programmation orientée objet
J La programmation procédurale maintient une séparation stricte entre code et données : Les variables d’une part, les
procédures d’autre part. Les procédures utilisent les valeurs des variables ou agissent sur les variables.
Or, il se trouve que beaucoup d’opérations sont communes à des objets du même type.
Dans un langage orienté objet, les opérations sont dé…nies en tant que propriété de l’objet. Python utilise la “dot
syntax” pour accéder aux attributs des objets, selon la syntaxe objet.methode(arguments).
(
a = a + [0]
Considérons les deux instructions suivantes sur la liste a :
a.append(0)
Dans les deux cas, l’e¤et est d’ajouter à la liste a l’élément 0 en …n de liste. Dans la seconde instruction, on applique la
méthode append à l’objet a, qui ici est une liste.
Dans le premier cas, pour créer la liste a + [0], Python crée une copie de a, auquelle il ajoute l’élément 0. Le coût est
proportionnel à la longueur de a, et l’adresse de a est donc modi…ée par l’instruction a = a + [0].
En revanche, l’instruction a.append(0) opère directement sur la liste référencée par a en lui ajoutant 0. Le coût est
constant (en fait en moyenne), et l’adresse de a n’est pas modi…ée par l’instruction a.append(0).
Remarque : Considérons les deux fonctions :
def f(a) :
a = a + [0]
def g(a) :
a.append(0)
Autrement dit, l’instruction a = a + [0] dans f crée une variable locale a : ainsi, la variable globale a n’est pas modi…ée).
En revanche, a est un objet dans g, qui est modi…é par la procédure g (les opérations sont e¤ectuées sur l’objet, et plus
précisement sur la valeur associée à la référence de a).
J Une méthode n’est en fait rien d’autre qu’une fonction, mais une fonction qui est associée à un objet. Elle fait partie
de la dé…nition de cet objet, ou plus précisément de la classe particulière à laquelle cet objet appartient (nous étudierons
ce concept de classe plus tard).
Mettre en oeuvre une méthode associée à un objet consiste en quelque sorte à faire « fonctionner » cet objet d’une
manière particulière. Par exemple, on met en oeuvre la méthode methode4(self,...) d’un objet objet3, à l’aide d’une
instruction du type : objet3.methode4(...) , c’est-àdire le nom de l’objet, puis le nom de la méthode, reliés l’un à
l’autre par un point. Ce point joue un rôle essentiel : on peut le considérer comme un véritable opérateur.
Dans l’exemple ci-dessus, en utilisant a.append(0), on applique la méthode append à l’objet a qui est ici une liste. En
l’occurrence, la méthode append() qui est en fait une fonction spéci…que des listes, a pour e¤et d’ajouter un élément par
la …n. L’élément à ajouter est transmis entre parenthèses, comme tout argument qui se respecte.
On obtient un résultat similaire si on utilise à la place a = a + [b].
Ce procédé est moins e¢ cace, car elle consiste à redé…nir à chaque itération de la boucle une nouvelle liste a, dans laquelle
la totalité de la liste précédente est à chaque fois recopiée avant l’ajout d’un élément supplémentaire.
En revanche, lorsque l’on utilise la méthode append(), l’ordinateur procède bel et bien à une modi…cation de la liste
existante (sans la recopier dans une nouvelle variable). Cette technique s’e¤ectue (en moyenne) en temps constant.
Dé…nition d’une classe d’objets et de ses attributs en programmation orientée objet
J Dans l’exemple suivant, on dé…nit une nouvelle classe d’objets, appelés complexe. Les attributs d’un complexe sont
dé…nis à l’aide de la fonction spéciale __init__ (qu’il faut écrire avec une paire de double _ ). Les attributs d’un objets
sont écrits sous la forme objet.attribut. Le mot-clé self représente l’objet qui sera utilisé ultérieurement.
class complexe :
def __init__(self,x,y) :
self.re = x ; self.im = y
def module(self) :
return (self.re**2+self.im**2)**(1/2)
Ainsi, on dé…nit ici deux attributs re et im aux objets de la classe complexe. On peut aussi dé…nir des fonctions (appelées
méthodes) qui sont elles aussi attachées aux objets de la classe complexe. Ainsi en est-il de la fonction module.
Une fois dé…ni la nouvelle classe d’objets et ses attributs, on peut dé…nir un objet :
z = complexe(3,4)
print(z.re)
# a¢ che 3
print(z.module())
# a¢ che 5:0
J Les objets sont munis d’attributs mais aussi de fonctions (appelées méthodes).
Les méthodes sont classiques ou spéciales. Dans les deux cas, elles sont dé…nies par def et ont self comme premier
argument, qui représente l’objet de la classe. Les méthodes spéciales sont reconnues par le fait que leur nom est précédé
et suivi par des doubles underscore. Les méthodes spéciales correspondent à des constructeurs. C’est notamment le cas
de la première méthode toujours nommée __init__ qui dé…nit les attributs des objets.
Une fonction dé…nie par def nom(self,x1,...,xn) est ensuite appelée par objet.nom(x1,...,xn).
On peut aussi dé…nir des objets de structure récursive. Par exemple :
J Piles (d’entiers)
Il s’agit d’une structure d’objet, dé…nie de la façon suivante :
- Une pile est soit vide ( = False) soit formée d’une tête (entier) et d’une queue (qui est une pile).
- Les deux opérations possibles sur les piles sont l’ajout d’un élément en tête de pile et la suppression de la tête de pile
(lorsque la pile n’est pas vide). L’ajout est une procédure et la suppression est une procédure-fonction renvoyant la valeur
de l’élément supprimé.
Remarque : En Python, cette structure est en fait déjà contenue dans la structure de liste.
class pile :
def __init__(self,x = None,q = None) :
self.tete = x ; self.queue = q
def append(self,x) :
self.tete , self.queue = x , self
def pop(self) :
if self.tete != None :
self.tete , self.queue = (self.queue).tete , (self.queue).queue
Par exemple, on peut considérer :
a = pile() ; a.append(2) ;
# ici, a.queue alors False et a.tete vaut 2
b = pile(2,pile(3,pile())) ;
b.pop() renvoie 2 et b vaut pile(3,pile())
Téléchargement