In [1]:
from __future__ import print_function

Structures de données

Plus de détails sur les listes

Le type de données liste possède d’autres méthodes. Voici toutes les méthodes des objets listes :

append(x) équivalent à a.insert(len(a), x).

extend(L) rallonge la liste en ajoutant à la fin tous les éléments de la liste donnée ; équivaut à a[len(a):] = L.

insert(i, x) insère un élément à une position donnée. Le premier argument est l’indice de l’élément avant lequel il faut insérer, donc a.insert(0, x) insère au début de la liste, et a.insert(len(a), x) est équivalent à a.append(x).

remove(x) enlève le premier élément de la liste dont la valeur est x. Il y a erreur si cet élément n’existe pas.

pop([i ]) enlève l’élément présent à la position donnée dans la liste, et le renvoie. Si aucun indice n’est spécifié, a.pop() renvoie le dernier élément de la liste. L’élément est aussi supprimé de la liste.

index(x) retourne l’indice dans la liste du premier élément dont la valeur est x. Il y a erreur si cet élément n’existe pas.

count(x) renvoie le nombre de fois que x apparaît dans la liste.

sort() trie les éléments à l’intérieur de la liste.

reverse() renverse l’ordre des éléments à l’intérieur de la liste.

Un exemple qui utilise toutes les méthodes des listes :


In [1]:
ma_liste = [66.6, 333, 333, 1, 1234.5]
print (ma_liste.count(333), ma_liste.count(66.6), ma_liste.count('x'))


2 1 0

In [2]:
ma_liste2 = list(ma_liste)
ma_liste2.sort()
print (ma_liste2)


[1, 66.6, 333, 333, 1234.5]

In [3]:
ma_liste.insert(2, -1)

In [4]:
ma_liste.append(333)

In [5]:
ma_liste


Out[5]:
[66.6, 333, -1, 333, 1, 1234.5, 333]

In [7]:
ma_liste.index(333)


Out[7]:
1

In [8]:
ma_liste.remove(333)
print(ma_liste)


[66.6, -1, 333, 1, 1234.5, 333]

In [9]:
ma_liste.reverse()
ma_liste


Out[9]:
[333, 1234.5, 1, 333, -1, 66.6]

In [10]:
ma_liste.sort()
ma_liste


Out[10]:
[-1, 1, 66.6, 333, 333, 1234.5]

Utiliser les listes comme des piles (*)

Les méthodes des listes rendent très facile l’utilisation d’une liste comme une pile, où le dernier élément ajouté est le premier élément récupéré (LIFO, "last-in, first-out"). Pour ajouter un élément au sommet de la pile, utilisez la méthode append(). Pour récupérer un élément du sommet de la pile, utilisez pop() sans indice explicite. Par exemple :


In [11]:
pile = [3, 4, 5]
pile.append(6)
pile.append(7)
pile


Out[11]:
[3, 4, 5, 6, 7]

In [12]:
pile.pop()


Out[12]:
7

In [13]:
pile


Out[13]:
[3, 4, 5, 6]

In [14]:
pile.pop()


Out[14]:
6

In [15]:
pile.pop()


Out[15]:
5

In [16]:
pile


Out[16]:
[3, 4]

In [17]:
type(pile)


Out[17]:
list

Utiliser les listes comme des files (*)

Vous pouvez aussi utiliser facilement une liste comme une file, où le premier élément ajouté est le premier élément retiré (FIFO, "first-in, first-out"). Pour ajouter un élément à la fin de la file, utiliser append(). Pour récupérer un élément du devant de la file, utilisez pop() avec 0 pour indice. Par exemple :


In [18]:
file = ["Eric", "John", "Michael"]
file.append("Terry")                # Terry arrive
file.append("Graham")               # Graham arrive
file.pop(0)


Out[18]:
'Eric'

In [19]:
file.pop(0)


Out[19]:
'John'

In [20]:
file


Out[20]:
['Michael', 'Terry', 'Graham']

Outils de programmation fonctionnelle (*)

Il y a trois fonctions intégrées qui sont très pratiques avec les listes : filter(), map(), et reduce().

'filter(fonction, sequence)' renvoit une liste (du même type, si possible) contenant les seul éléments de la séquence pour lesquels fonction(element) est vraie. Par exemple, pour calculer quelques nombres premiers :


In [21]:
def f(x): return x % 2 != 0 and x % 3 != 0

filter(f, range(2, 25))


Out[21]:
[5, 7, 11, 13, 17, 19, 23]

'map(fonction, sequence)' appelle fonction(element) pour chacun des éléments de la séquence et renvoie la liste des valeurs de retour. Par exemple, pour calculer les cubes :


In [22]:
def cube(x): return x*x*x

map(cube, range(1, 11))


Out[22]:
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

Plusieurs séquences peuvent être passées en paramètre ; la fonction doit alors avoir autant d’arguments qu’il y a de séquences et est appelée avec les éléments correspondants de chacune des séquences (ou None si l’une des séquences est plus courte que l’autre). Si None est passé en tant que fonction, une fonction retournant ses arguments lui est substituée.

'reduce(fonction, sequence)' renvoie une valeur unique construite par l’appel de la fonction binaire fonction sur les deux premiers éléments de la séquence, puis sur le résultat et l’élément suivant, et ainsi de suite. Par exemple, pour calculer la somme des nombres de 1 à 10 :


In [23]:
def ajoute(x,y): return x+y

reduce(ajoute, range(1, 11))


Out[23]:
55

S’il y a seulement un élément dans la séquence, sa valeur est renvoyée ; si la séquence est vide, une exception est déclenchée.

Un troisième argument peut être transmis pour indiquer la valeur de départ. Dans ce cas, la valeur de départ est renvoyée pour une séquence vide, et la fonction est d’abord appliquée à la valeur de départ et au premier élément de la séquence, puis au résultat et à l’élément suivant, et ainsi de suite. Par exemple,


In [24]:
def somme(seq):
    def ajoute(x,y): return x+y
    return reduce(ajoute, seq, 0)

somme(range(1, 11))


Out[24]:
55

In [25]:
somme([])


Out[25]:
0

List Comprehensions (*)

Les list comprehensions fournissent une façon concise de créer des listes sans avoir recours à map(), filter() et/ou lambda. La définition de liste qui en résulte a souvent tendance à être plus claire que des listes construites avec ces outils. Chaque list comprehension consiste en une expression suivie d’une clause for, puis zéro ou plus clauses for ou if. Le résultat sera une liste résultant de l’évaluation de l’expression dans le contexte des clauses for et if qui la suivent. Si l’expression s’évalue en un tuple, elle doit être mise entre parenthèses.


In [26]:
liste_de_fruits = ['  banane', '  myrtille ', 'fruit de la passion  ']
nouvelle_liste_de_fruits = [fruit.strip() for fruit in liste_de_fruits]
print (nouvelle_liste_de_fruits)


['banane', 'myrtille', 'fruit de la passion']

In [27]:
vec = [2, 4, 6]
[3*x for x in vec]


Out[27]:
[6, 12, 18]

In [28]:
[3*x for x in vec if x > 3]


Out[28]:
[12, 18]

In [29]:
[3*x for x in vec if x <= 2]


Out[29]:
[6]

In [30]:
[{x: x**2} for x in vec]


Out[30]:
[{2: 4}, {4: 16}, {6: 36}]

In [31]:
[[x,x**2] for x in vec]


Out[31]:
[[2, 4], [4, 16], [6, 36]]

In [32]:
[x, x**2 for x in vec] # erreur : parenthèses obligatoires pour les tuples


  File "<ipython-input-32-7a0f143b733f>", line 1
    [x, x**2 for x in vec] # erreur : parenthèses obligatoires pour les tuples
               ^
SyntaxError: invalid syntax

In [33]:
[(x, x**2) for x in vec]


Out[33]:
[(2, 4), (4, 16), (6, 36)]

In [34]:
vec1 = [2, 4, 6]
vec2 = [4, 3, -9]
[x*y for x in vec1 for y in vec2]


Out[34]:
[8, 6, -18, 16, 12, -36, 24, 18, -54]

In [35]:
[x+y for x in vec1 for y in vec2]


Out[35]:
[6, 5, -7, 8, 7, -5, 10, 9, -3]

In [36]:
[vec1[i]*vec2[i] for i in range(len(vec1))]


Out[36]:
[8, 12, -54]

L'instruction del

Il y a un moyen d’enlever un élément d’une liste en ayant son indice au lieu de sa valeur : l’instruction del. Cela peut aussi être utilisé pour enlever des tranches dans une liste (ce que l’on a fait précédemment par remplacement de la tranche par une liste vide). Par exemple :


In [37]:
a = [-1, 1, 66.6, 333, 333, 1234.5]
del a[0]
a


Out[37]:
[1, 66.6, 333, 333, 1234.5]

In [38]:
del a[2:4]
a


Out[38]:
[1, 66.6, 1234.5]

del peut aussi être utilisé pour supprimer des variables complètes :


In [39]:
del a

Faire par la suite référence au nom a est une erreur (au moins jusqu’à ce qu’une autre valeur ne lui soit affectée). Nous trouverons d’autres utilisations de del plus tard.

N-uplets (tuples) et séquences

Nous avons vu que les listes et les chaînes ont plusieurs propriétés communes, telles que l’indexation et les opérations de découpage. Elles sont deux exemples de types de données de type séquence. Puisque Python est un langage qui évolue, d’autres types de données de type séquence pourraient être ajoutés. Il y a aussi un autre type de données de type séquence standard : le tuple (ou n-uplet).

Un n-uplet consiste en un ensemble de valeurs séparées par des virgules, par exemple :


In [40]:
t = 12345, 54321, 'salut!'
t[0]


Out[40]:
12345

In [41]:
t


Out[41]:
(12345, 54321, 'salut!')

In [42]:
# Les tuples peuvent être imbriqués:
u = t, (1, 2, 3, 4, 5)
u


Out[42]:
((12345, 54321, 'salut!'), (1, 2, 3, 4, 5))

Comme vous pouvez le voir, à l’affichage, les tuples sont toujours entre parenthèses, de façon à ce que des tuples de tuples puissent être interprétés correctement ; ils peuvent être saisis avec ou sans parenthèses, bien que des parenthèses soient souvent nécessaires (si le tuple fait partie d’une expression plus complexe).

Les tuples ont plein d’utilisations. Par exemple, les couples de coordonnées (x, y), les enregistrements des employés d’une base de données, etc. Les tuples, comme les chaînes, sont non-modifiables : il est impossible d’affecter individuellement une valeur aux éléments d’un tuple (bien que vous puissiez simuler quasiment cela avec le découpage et la concaténation).

spécificités des tuples (*)

Un problème particulier consiste à créer des tuples contenant 0 ou 1 élément : la syntaxe reconnaît quelques subtilités pour y arriver. Les tuples vides sont construits grâce à des parenthèses vides ; un tuple avec un élément est construit en faisant suivre une valeur d’une virgule (il ne suffit pas de mettre une valeur seule entre parenthèses). Peu lisible, mais efficace. Par exemple :


In [43]:
empty = ()
singleton = 'salut', # <-- notez la virgule en fin de ligne
len(empty)


Out[43]:
0

In [44]:
len(singleton)


Out[44]:
1

In [45]:
singleton


Out[45]:
('salut',)

L’instruction t = 12345, 54321, 'salut !' est un exemple d’ emballage en tuple (tuple packing) : les valeurs 12345, 54321 et 'salut !' sont emballées ensemble dans un tuple. L’opération inverse est aussi possible :


In [46]:
x, y, z = t

Cela est appelé, fort judicieusement, déballage de tuple (tuple unpacking). Le déballage d’un tuple nécessite que la liste des variables à gauche ait un nombre d’éléments égal à la longueur du tuple. Notez que des affectations multiples ne sont en réalité qu’une combinaison d’emballage et déballage de tuples !

Ensembles (*)

Python comporte également un type de données pour représenter des ensembles. Un set est une collection (non rangée) sans éléments dupliqués. Les emplois basiques sont le test d’appartenance et l’élimination des entrée dupliquées. Les objets ensembles supportent les opérations mathématiques comme l’union, l’intersection, la différence et la différence symétrique. Voici une démonstration succincte :


In [47]:
panier = ['pomme', 'orange', 'pomme', 'poire', 'orange', 'banane']
fruits = set(panier) # creation d'un set sans éléments dupliqués
fruits


Out[47]:
{'banane', 'orange', 'poire', 'pomme'}

In [48]:
'orange' in fruits # test d'appartenance rapide


Out[48]:
True

In [49]:
'ananas' in fruits


Out[49]:
False

Une autre démonstration rapide des ensembles sur les lettres uniques de deux mots


In [50]:
a = set('abracadabra')
b = set('alacazam')
a                       # lettres uniques dans abracadabra


Out[50]:
{'a', 'b', 'c', 'd', 'r'}

In [51]:
b                       # lettres uniques dans alacazam


Out[51]:
{'a', 'c', 'l', 'm', 'z'}

In [52]:
a - b                   # lettres dans a mais pas dans b


Out[52]:
{'b', 'd', 'r'}

In [53]:
a | b                   # lettres soit dans a ou b


Out[53]:
{'a', 'b', 'c', 'd', 'l', 'm', 'r', 'z'}

In [54]:
a & b                   # lettres dans a et b


Out[54]:
{'a', 'c'}

In [55]:
a ^ b                   # lettres dans a ou dans b mais pas dans les deux


Out[55]:
{'b', 'd', 'l', 'm', 'r', 'z'}

Dictionnaires

Un autre type de données intégré à Python est le dictionnaire. Les dictionnaires sont parfois trouvés dans d’autres langages sous le nom de "mémoires associatives" ou "tableaux associatifs". A la différence des séquences, qui sont indexées par un intervalle numérique, les dictionnaires sont indexés par des clés, qui peuvent être de n’importe quel type non-modifiable ; les chaînes et les nombres peuvent toujours être des clés. Les tuples peuvent être utilisés comme clés s’ils ne contiennent que des chaînes, des nombres ou des tuples. Vous ne pouvez pas utiliser des listes comme clés, puisque les listes peuvent être modifiées en utilisant leur méthode append().

Il est préférable de considérer les dictionnaires comme des ensembles non ordonnés de couples clé:valeur, avec la contrainte que les clés soient uniques (à l’intérieur d’un même dictionnaire). Un couple d’accolades crée un dictionnaire vide : {}. Placer une liste de couples clé:valeur séparés par des virgules à l’intérieur des accolades ajoute les couples initiaux clé :valeur au dictionnaire ; c’est aussi de cette façon que les dictionnaires sont affichés.

Les opérations principales sur un dictionnaire sont le stockage d’une valeur à l’aide d’une certaine clé et l’extraction de la valeur en donnant la clé. Il est aussi possible de détruire des couples clé:valeur avec del. Si vous stockez avec une clé déjà utilisée, l’ancienne valeur associée à cette clé est oubliée. C’est une erreur d’extraire une valeur en utilisant une clé qui n’existe pas.

La méthode keys() d’un objet de type dictionnaire retourne une liste de toutes les clés utilisées dans le dictionnaire, dans un ordre quelconque (si vous voulez qu’elle soit triée, appliquez juste la méthode sort() à la liste des clés). Pour savoir si une clé particulière est dans le dictionnaire, utilisez la méthode has_key() du dictionnaire. Voici un petit exemple utilisant un dictionnaire :


In [6]:
tel = {'jack': 4098, 'sape': 4139}
tel['guido'] = 4127
tel


Out[6]:
{'jack': 4098, 'sape': 4139, 'guido': 4127}

In [7]:
tel['jack']


Out[7]:
4098

In [58]:
del (tel['sape'])
tel['irv'] = 4127
tel


Out[58]:
{'guido': 4127, 'irv': 4127, 'jack': 4098}

In [59]:
tel.keys()


Out[59]:
['jack', 'irv', 'guido']

In [60]:
tel.has_key('guido')


Out[60]:
True

Le constructeur dict() construit des dictionnaires directement à partir de listes de paires clé:valeur rangées comme des n-uplets. Lorsque les paires forment un motif, les list comprehensions peuvent spécifier de manière compacte la liste de clés-valeurs.


In [61]:
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])


Out[61]:
{'guido': 4127, 'jack': 4098, 'sape': 4139}

In [62]:
dict([(x, x**2) for x in (2, 4, 6)]) # utilisation de la list comprehension


Out[62]:
{2: 4, 4: 16, 6: 36}

Lorsque les clés sont de simples chaînes il est parfois plus simple de spécifier les paires en utilisant des arguments à mot-clé :


In [63]:
dict(sape=4139, guido=4127, jack=4098)


Out[63]:
{'guido': 4127, 'jack': 4098, 'sape': 4139}

Techniques de boucles

Lorsqu’on boucle sur un dictionnaire, les clés et les valeurs correspondantes peuvent être obtenues en même temps en utilisant la méthode iteritems()


In [64]:
chevaliers = {'gallahad': 'le pur', 'robin': 'le brave'}
for c, v in chevaliers.iteritems():
    print (c, v)


gallahad le pur
robin le brave

Lorsqu’on boucle sur une séquence, l’indice donnant la position et la valeur correspondante peuvent être obtenus en même temps en utilisant la fonction enumerate().


In [65]:
for i, v in enumerate(['tic', 'tac', 'toe']):
    print (i, v)


0 tic
1 tac
2 toe

Pour boucler sur deux séquences, ou plus, en même temps, les éléments peuvent être appariés avec la fonction zip().


In [66]:
questions = ['nom', 'but', 'drapeau']
reponses = ['lancelot', 'le sacre graal', 'le bleu']
for q, r in zip(questions, reponses):
    print ("Quel est ton %s? C'est %s." % (q, r))


Quel est ton nom? C'est lancelot.
Quel est ton but? C'est le sacre graal.
Quel est ton drapeau? C'est le bleu.

Pour boucler à l’envers sur une séquence, spécifiez d’abord la séquence à l’endroit, ensuite appelez la fonction reversed().


In [67]:
for i in reversed(xrange(1,10,2)):
    print (i)


9
7
5
3
1

Pour boucler sur une séquence comme si elle était triée, utilisez la fonction sorted() qui retourne une liste nouvelle triée tout en laissant la source inchangée.


In [68]:
panier = ['pomme', 'orange', 'pomme', 'poire', 'orange', 'banane']
for f in sorted(set(panier)):
    print (f)


banane
orange
poire
pomme

Plus de détails sur les conditions (*)

Les conditions utilisées dans les instructions while et if peuvent contenir d’autres opérateurs en dehors des comparaisons.

Les opérateurs de comparaison in et not in vérifient si une valeur apparaît (ou non) dans une séquence. Les opérateurs is et is not vérifient si deux objets sont réellement le même objet ; cela se justifie seulement pour les objets modifiables comme les listes. Tous les opérateurs de comparaison ont la même priorité, qui est plus faible que celle de tous les opérateurs numériques.

Les comparaisons peuvent être enchaînées. Par exemple, a < b == c teste si a est strictement inférieur à b et de plus si b est égal à c.

Les comparaisons peuvent être combinées avec les opérateurs booléens and (et) et or (ou), et le résultat d’une comparaison (ou de n’importe quel autre expression Booléenne) peut être inversé avec not (pas). Ces opérateurs ont encore une fois une priorité inférieure à celle des opérateurs de comparaison ; et entre eux, not a la plus haute priorité, et or la plus faible, de sorte que A and not B or C est équivalent à (A and (not B)) or C. Bien sûr, les parenthèses peuvent être utilisées pour exprimer les compositions désirées.

Les opérateurs booléens and et or sont des opérateurs dits court-circuit : leurs arguments sont évalués de gauche à droite, et l’évaluation s’arrête dès que le résultat est trouvé. Par exemple, si A et C sont vrais mais que B est faux, A and B and C n'évalue pas l'expression C. En général, la valeur de retour d'un opérateur court-circuit, quand elle est utilisée comme une valeur générale et non comme un booléen, est celle du dernier argument évalué.

Il est possible d’affecter le résultat d’une comparaison ou une autre expression booléenne à une variable. Par exemple


In [69]:
chaine1, chaine2, chaine3 = '', 'Trondheim', 'Hammer Dance'
non_null = chaine1 or chaine2 or chaine3
non_null


Out[69]:
'Trondheim'

Notez qu’en Python, au contraire du C, les affectations ne peuvent pas être effectuées à l’intérieur des expressions. Les programmeurs C ronchonneront peut-être, mais cela évite une classe de problèmes qu’on rencontre dans les programmes C : écrire = dans une expression alors qu’il fallait ==.

Comparer les séquences et d’autres types (*)

Les objets de type séquence peuvent être comparés à d’autres objets appartenant au même type de séquence. La comparaison utilise l’ordre lexicographique : les deux premiers éléments sont d’abord comparés, et s’ils diffèrent cela détermine le résultat de la comparaison ; s’ils sont égaux, les deux éléments suivants sont comparés, et ainsi de suite, jusqu’à ce que l’une des deux séquences soit épuisée. Si deux éléments à comparer sont eux-mêmes des séquences du même type, la comparaison lexicographique est reconsidérée récursivement. Si la comparaison de tous les éléments de deux séquences les donne égaux, les séquences sont considérées comme égales. Si une séquence est une sous-séquence initiale de l’autre, la séquence la plus courte est la plus petite (inférieure). L’ordonnancement lexicographique pour les chaînes utilise l’ordonnancement ASCII pour les caractères. Quelques exemples de comparaisons de séquences du même type :


In [70]:
(1, 2, 3) < (1, 2, 4)


Out[70]:
True

In [71]:
[1, 2, 3] < [1, 2, 4]


Out[71]:
True

In [72]:
'ABC' < 'C' < 'Pascal' < 'Python'


Out[72]:
True

In [73]:
(1, 2, 3, 4) < (1, 2, 4)


Out[73]:
True

In [74]:
(1, 2) < (1, 2, -1)


Out[74]:
True

In [75]:
(1, 2, 3) == (1.0, 2.0, 3.0)


Out[75]:
True

In [76]:
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)


Out[76]:
True

Notez que la comparaison d’objets de types différents est licite. Le résultat est déterministe mais arbitraire : les types sont triés selon leur nom. Ainsi une liste (list) est toujours inférieure à une chaîne (string), une chaîne (string) est toujours inférieure à un n-uplet (tuple), etc. Les types numériques mélangés sont comparés en fonction de leur valeur numérique, ainsi 0 est égal à 0.0, etc.

Ce notebook est une adaptation de la traduction française, dirigée par Olivier Berger et mise à jour par Henri Garreta, du tutoriel Python édité par Guido van Rossum et Fred L. Drake.