5. Structures de données
************************

Ce chapitre reprend plus en détail quelques points déjà décrits
précédemment et introduit également de nouvelles notions.


5.1. Compléments sur les listes
===============================

Le type liste dispose de méthodes supplémentaires. Voici toutes les
méthodes des objets de type liste :

list.append(x)

   Ajoute un élément à la fin de la liste ; équivalent à "a[len(a):] =
   [x]".

list.extend(L)

   Étend la liste en y ajoutant tous les éléments de la liste fournie
   ; équivalent à "a[len(a):] = L".

list.insert(i, x)

   Insère un élément à la position indiquée. Le premier argument est
   la position de l’élément courant avant lequel l’insertion doit
   s’effectuer, donc "a.insert(0, x)" insère l’élément en tête de la
   liste et "a.insert(len(a), x)" est équivalent à "a.append(x)".

list.remove(x)

   Supprime de la liste le premier élément dont la valeur est *x*. Une
   exception est levée s’il existe aucun élément avec cette valeur.

list.pop([i])

   Enlève de la liste l’élément situé à la position indiquée et le
   renvoie en valeur de retour. Si aucune position n’est spécifiée,
   "a.pop()" enlève et renvoie le dernier élément de la liste (les
   crochets autour du *i* dans la signature de la méthode indiquent
   que ce paramètre est facultatif et non que vous devez placer des
   crochets dans votre code ! Vous retrouverez cette notation
   fréquemment dans le Guide de Référence de la Bibliothèque Python).

list.index(x)

   Retourne la position du premier élément de la liste ayant la valeur
   *x*. Une exception est levée s’il n’existe aucun élément avec cette
   valeur.

list.count(x)

   Renvoie le nombre d’éléments ayant la valeur *x* dans la liste.

list.sort(cmp=None, key=None, reverse=False)

   Classe les éléments sur place (les arguments peuvent personnaliser
   le classement, voir "sorted()" pour leur explication).

list.reverse()

   Inverse l’ordre des éléments de la liste, en place.

L’exemple suivant utilise la plupart des méthodes des listes :

   >>> a = [66.25, 333, 333, 1, 1234.5]
   >>> print a.count(333), a.count(66.25), a.count('x')
   2 1 0
   >>> a.insert(2, -1)
   >>> a.append(333)
   >>> a
   [66.25, 333, -1, 333, 1, 1234.5, 333]
   >>> a.index(333)
   1
   >>> a.remove(333)
   >>> a
   [66.25, -1, 333, 1, 1234.5, 333]
   >>> a.reverse()
   >>> a
   [333, 1234.5, 1, 333, -1, 66.25]
   >>> a.sort()
   >>> a
   [-1, 1, 66.25, 333, 333, 1234.5]
   >>> a.pop()
   1234.5
   >>> a
   [-1, 1, 66.25, 333, 333]

You might have noticed that methods like "insert", "remove" or "sort"
that only modify the list have no return value printed – they return
the default "None".  This is a design principle for all mutable data
structures in Python.


5.1.1. Utilisation des listes comme des piles
---------------------------------------------

Les méthodes des listes rendent très facile leur utilisation comme des
piles, où le dernier élément ajouté est le premier récupéré (« dernier
entré, premier sorti » ou LIFO pour *last-in, first-out* en anglais).
Pour ajouter un élément sur la pile, utilisez la méthode "append()".
Pour récupérer l’objet au sommet de la pile, utilisez la méthode
"pop()" sans indicateur de position. Par exemple :

   >>> stack = [3, 4, 5]
   >>> stack.append(6)
   >>> stack.append(7)
   >>> stack
   [3, 4, 5, 6, 7]
   >>> stack.pop()
   7
   >>> stack
   [3, 4, 5, 6]
   >>> stack.pop()
   6
   >>> stack.pop()
   5
   >>> stack
   [3, 4]


5.1.2. Utilisation des listes comme des files
---------------------------------------------

Il est également possible d’utiliser une liste comme une file, où le
premier élément ajouté est le premier récupéré (« premier entré,
premier sorti » ou FIFO pour *first-in, first-out*) ; toutefois, les
listes ne sont pas très efficaces pour réaliser ce type de traitement.
Alors que les ajouts et suppressions en fin de liste sont rapides, les
opérations d’insertions ou de retraits en début de liste sont lentes
(car tous les autres éléments doivent être décalés d’une position).

Pour implémenter une file, utilisez la classe "collections.deque" qui
a été conçue pour réaliser rapidement les opérations d’ajouts et de
retraits aux deux extrémités. Par exemple :

   >>> from collections import deque
   >>> queue = deque(["Eric", "John", "Michael"])
   >>> queue.append("Terry")           # Terry arrives
   >>> queue.append("Graham")          # Graham arrives
   >>> queue.popleft()                 # The first to arrive now leaves
   'Eric'
   >>> queue.popleft()                 # The second to arrive now leaves
   'John'
   >>> queue                           # Remaining queue in order of arrival
   deque(['Michael', 'Terry', 'Graham'])


5.1.3. Outils de programmation fonctionnelle
--------------------------------------------

Il existe trois fonctions primitives qui sont très utiles lorsqu’elles
sont utilisées avec des listes : "filter()", "map()" et "reduce()".

"filter(function, sequence)" retourne une séquence composée des
éléments de la séquence initiale pour lesquels la fonction
"function(item)" est vraie. Si *sequence* est de type "str",
"unicode", ou "tuple", le résultat sera du même type ; sinon, il sera
toujours de type "list". Par exemple, pour construire une séquence de
nombres divisibles par 2 ou 5

   >>> def f(x): return x % 3 == 0 or x % 5 == 0
   ...
   >>> filter(f, range(2, 25))
   [3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24]

"map(function, sequence)" appelle "function(item)" pour chaque élément
de la séquence et retourne une liste contenant l’ensemble des
résultats. Par exemple, pour calculer des cubes

   >>> def cube(x): return x*x*x
   ...
   >>> map(cube, range(1, 11))
   [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

Plus d’une séquence peut être passée en paramètre ; dans ce cas, la
fonction doit avoir autant d’arguments qu’il y a de séquences, et elle
sera appelée avec l’élément correspondant de chaque séquence (ou
"None" si certaines séquences sont plus courtes que d’autres).

   >>> seq = range(8)
   >>> def add(x, y): return x+y
   ...
   >>> map(add, seq, seq)
   [0, 2, 4, 6, 8, 10, 12, 14]

"reduce(function, sequence)" retourne une seule valeur construire en
appelant la fonction *function* avec comme paramètres les deux
premiers éléments de la séquence, puis le premier résultat et
l’élément suivant, et ainsi de suite. Par exemple, pour calculer la
somme des nombres de 1 à 10

   >>> def add(x,y): return x+y
   ...
   >>> reduce(add, range(1, 11))
   55

S’il n’y a qu’un seul élément dans la séquence, sa valeur est
retournée ; si la séquence est vide, une exception est levée.

Un troisième paramètre peut être ajouté pour indiquer la valeur de
départ. Dans ce cas, cette valeur est retournée dans le cas d’une
séquence vide ; sinon, la fonction est d’abord appelée avec comme
paramètres cette valeur de départ et le premier élément de la
séquence, puis ce premier résultat et l’élément suivant, et ainsi de
suite. Par exemple

   >>> def sum(seq):
   ...     def add(x,y): return x+y
   ...     return reduce(add, seq, 0)
   ...
   >>> sum(range(1, 11))
   55
   >>> sum([])
   0

N’utilisez pas la fonction "sum()" fournie dans cet exemple. Effectuer
des sommes de nombres est un besoin tellement courant qu’il existe une
fonction native "sum(sequence)" qui fait exactement la même chose.


5.1.4. Compréhensions de listes
-------------------------------

Les compréhensions de listes fournissent un moyen de construire des
listes de manière très concise. Une application classique est la
construction de nouvelles listes où chaque élément est le résultat
d’une opération appliquée à chaque élément d’une autre séquence ; ou
de créer une sous-séquence des éléments satisfaisant une condition
spécifique.

Par exemple, supposons que l’on veuille créer une liste de carrés,
comme :

   >>> squares = []
   >>> for x in range(10):
   ...     squares.append(x**2)
   ...
   >>> squares
   [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

On peut obtenir le même résultat avec

   squares = [x**2 for x in range(10)]

C’est également l’équivalent de "squares = map(lambda x: x**2,
range(10))", mais en plus concis et plus lisible.

Une compréhension de liste consiste à placer entre crochets une
expression suivie par une clause "for" puis par zéro ou plus clauses
"for" ou "if". Le résultat est une nouvelle liste résultat de
l’évaluation de l’expression dans le contexte des clauses "for" et
"if" qui la suivent. Par exemple, cette compréhension de liste combine
les éléments de deux listes s’ils ne sont pas égaux :

   >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
   [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

et c’est équivaent à :

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Notez que l’ordre des instructions "for" et "if" est le même dans ces
différents extraits de code.

Si l’expression est un tuple (c’est-à-dire "(x, y)" dans cet exemple),
elle doit être entourée par des parenthèses :

   >>> vec = [-4, -2, 0, 2, 4]
   >>> # create a new list with the values doubled
   >>> [x*2 for x in vec]
   [-8, -4, 0, 4, 8]
   >>> # filter the list to exclude negative numbers
   >>> [x for x in vec if x >= 0]
   [0, 2, 4]
   >>> # apply a function to all the elements
   >>> [abs(x) for x in vec]
   [4, 2, 0, 2, 4]
   >>> # call a method on each element
   >>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
   >>> [weapon.strip() for weapon in freshfruit]
   ['banana', 'loganberry', 'passion fruit']
   >>> # create a list of 2-tuples like (number, square)
   >>> [(x, x**2) for x in range(6)]
   [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
   >>> # the tuple must be parenthesized, otherwise an error is raised
   >>> [x, x**2 for x in range(6)]
     File "<stdin>", line 1, in <module>
       [x, x**2 for x in range(6)]
                  ^
   SyntaxError: invalid syntax
   >>> # flatten a list using a listcomp with two 'for'
   >>> vec = [[1,2,3], [4,5,6], [7,8,9]]
   >>> [num for elem in vec for num in elem]
   [1, 2, 3, 4, 5, 6, 7, 8, 9]

Les compréhensions de listes peuvent contenir des expressions
complexes et des fonctions imbriquées :

   >>> from math import pi
   >>> [str(round(pi, i)) for i in range(1, 6)]
   ['3.1', '3.14', '3.142', '3.1416', '3.14159']


5.1.4.1. Compréhensions de listes imbriquées
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

La première expression dans une compréhension de liste peut être
n’importe quelle expression, y compris une autre compréhension de
liste.

Voyez l’exemple suivant d’une matrice de 3 par 4, implémentée sous la
forme de 3 listes de 4 éléments :

   >>> matrix = [
   ...     [1, 2, 3, 4],
   ...     [5, 6, 7, 8],
   ...     [9, 10, 11, 12],
   ... ]

Cette compréhension de liste transpose les lignes et les colonnes :

   >>> [[row[i] for row in matrix] for i in range(4)]
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Comme nous l’avons vu dans la section précédente, la compréhension de
liste imbriquée est évaluée dans le contexte de l’instruction "for"
qui la suit, donc cet exemple est équivalent à :

   >>> transposed = []
   >>> for i in range(4):
   ...     transposed.append([row[i] for row in matrix])
   ...
   >>> transposed
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

lequel à son tour est équivalent à :

   >>> transposed = []
   >>> for i in range(4):
   ...     # the following 3 lines implement the nested listcomp
   ...     transposed_row = []
   ...     for row in matrix:
   ...         transposed_row.append(row[i])
   ...     transposed.append(transposed_row)
   ...
   >>> transposed
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Dans des cas concrets, il est toujours préférable d’utiliser des
fonctions natives plutôt que des instructions de contrôle de flux
complexes. La fonction "zip()" ferait dans ce cas un excellent travail
:

   >>> zip(*matrix)
   [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

Voyez Séparation des listes d’arguments pour plus de détails sur
l’astérisque de cette ligne.


5.2. L’instruction "del"
========================

Il existe un moyen de retirer un élément d’une liste à partir de sa
position au lieu de sa valeur : l’instruction "del". Elle diffère de
la méthode "pop()" qui, elle, renvoie une valeur. L’instruction "del"
peut également être utilisée pour supprimer des tranches d’une liste
ou la vider complètement (ce que nous avions fait auparavant en
affectant une liste vide à la tranche). Par exemple :

   >>> a = [-1, 1, 66.25, 333, 333, 1234.5]
   >>> del a[0]
   >>> a
   [1, 66.25, 333, 333, 1234.5]
   >>> del a[2:4]
   >>> a
   [1, 66.25, 1234.5]
   >>> del a[:]
   >>> a
   []

"del" peut aussi être utilisée pour supprimer des variables :

   >>> del a

À partir de là, référencer le nom "a" est une erreur (au moins jusqu’à
ce qu’une autre valeur lui soit affectée). Vous trouverez d’autres
utilisations de la fonction "del" plus tard.


5.3. Tuples et séquences
========================

Nous avons vu que les listes et les chaînes de caractères ont beaucoup
de propriétés en commun, comme l’indexation et les opérations sur des
tranches. Ce sont deux exemple de *séquences* (voir Sequence Types —
str, unicode, list, tuple, bytearray, buffer, xrange). Comme Python
est un langage en constante évolution, d’autres types de séquences y
seront peut-être ajoutés. Il existe également un autre type standard
de séquence : le *tuple*.

Un tuple consiste en différentes valeurs séparées par des virgules,
comme par exemple :

   >>> t = 12345, 54321, 'hello!'
   >>> t[0]
   12345
   >>> t
   (12345, 54321, 'hello!')
   >>> # Tuples may be nested:
   ... u = t, (1, 2, 3, 4, 5)
   >>> u
   ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
   >>> # Tuples are immutable:
   ... t[0] = 88888
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: 'tuple' object does not support item assignment
   >>> # but they can contain mutable objects:
   ... v = ([1, 2, 3], [3, 2, 1])
   >>> v
   ([1, 2, 3], [3, 2, 1])

Comme vous pouvez le voir, les tuples sont toujours affichés entre
parenthèses, de façon à ce que des tuples imbriqués soient interprétés
correctement ; ils peuvent être entrés avec ou sans parenthèses, même
si celles-ci sont souvent nécessaires (notamment lorsqu’un tuple fait
partie d’une expression plus longue). Il n’est pas possible d’affecter
de valeur à un élément d’un tuple ; par contre, il est possible de
créer des tuples contenant des objets muables, comme des listes.

Si les tuples peuvent sembler similaires aux listes, ils sont souvent
utilisés dans des cas différents et pour des raisons différentes. Les
tuples sont *immuable*s et contiennent souvent des séquences
hétérogènes d’éléments qui sont accédés par « déballage » (voir plus
loin) ou indexation (ou même par attributs dans le cas des
"namedtuples"). Les listes sont souvent *muable* et contiennent des
éléments homogènes qui sont accédés par itération sur la liste.

Un problème spécifique est la construction de tuples ne contenant
aucun ou un seul élément : la syntaxe a quelques tournures spécifiques
pour s’en accommoder. Les tuples vides sont construits par une paire
de parenthèses vides ; un tuple avec un seul élément est construit en
faisant suivre la valeur par une virgule (il n’est pas suffisant de
placer cette valeur entre parenthèses). Pas très joli, mais efficace.
Par exemple :

   >>> empty = ()
   >>> singleton = 'hello',    # <-- note trailing comma
   >>> len(empty)
   0
   >>> len(singleton)
   1
   >>> singleton
   ('hello',)

L’instruction "t = 12345, 54321, 'hello !'" est un exemple d’un
*emballage de tuple* : les valeurs "12345", "54321" et "hello !" sont
emballées ensemble dans un tuple. L’opération inverse est aussi
possible :

   >>> x, y, z = t

Ceci est appelé, de façon plus ou moins appropriée, un *déballage de
séquence* et fonctionne pour toute séquence placée à droite de
l’expression. Ce déballage requiert autant de variables dans la partie
gauche qu’il y a d’éléments dans la séquence. Notez également que
cette affectation multiple est juste une combinaison entre un
emballage de tuple et un déballage de séquence.


5.4. Ensembles
==============

Python fournit également un type de donnée pour les *ensembles*. Un
ensemble est une collection non ordonnée sans élément dupliqué. Des
utilisations basiques concernent par exemple des tests d’appartenance
ou des suppressions de doublons. Les ensembles savent également
effectuer les opérations mathématiques telles que les unions,
intersections, différences et différences symétriques.

Des accolades ou la fonction "set()" peuvent être utilisés pour créer
des ensembles.    Notez que pour créer un ensemble vide, "{}" ne
fonctionne pas, cela crée un dictionnaire vide. Utilisez plutôt
"set()".

Voici une brève démonstration :

   >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
   >>> fruit = set(basket)               # create a set without duplicates
   >>> fruit
   set(['orange', 'pear', 'apple', 'banana'])
   >>> 'orange' in fruit                 # fast membership testing
   True
   >>> 'crabgrass' in fruit
   False

   >>> # Demonstrate set operations on unique letters from two words
   ...
   >>> a = set('abracadabra')
   >>> b = set('alacazam')
   >>> a                                  # unique letters in a
   set(['a', 'r', 'b', 'c', 'd'])
   >>> a - b                              # letters in a but not in b
   set(['r', 'd', 'b'])
   >>> a | b                              # letters in either a or b
   set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
   >>> a & b                              # letters in both a and b
   set(['a', 'c'])
   >>> a ^ b                              # letters in a or b but not both
   set(['r', 'd', 'b', 'm', 'z', 'l'])

Tout comme pour les compréhensions de listes, il est possible d’écrire
des compréhensions d’ensembles :

   >>> a = {x for x in 'abracadabra' if x not in 'abc'}
   >>> a
   set(['r', 'd'])


5.5. Dictionnaires
==================

Un autre type de donnée très utile, natif dans Python, est le
*dictionnaire* (voir Les types de correspondances — dict). Ces
dictionnaires sont parfois présents dans d’autres langages sous le nom
de « mémoires associatives » ou de « tableaux associatifs ». À la
différence des séquences, qui sont indexées par des nombres, les
dictionnaires sont indexés par des *clés*, qui peuvent être de
n’importe quel type immuable ; les chaînes de caractères et les
nombres peuvent toujours être des clés. Des tuples peuvent être
utilisés comme clés s’ils ne contiennent que des chaînes, des nombres
ou des tuples ; si un tuple contient un objet muable, de façon directe
ou indirecte, il ne peut pas être utilisé comme une clé. Vous ne
pouvez pas utiliser des listes comme clés, car les listes peuvent être
modifiées en place en utilisant des affectations par position, par
tranches ou via des méthodes comme "append()" ou "extend()".

Le plus simple est de considérer les dictionnaires comme des ensembles
non ordonnés de paires *clé: valeur*, les clés devant être uniques (au
sein d’un dictionnaire). Une paire d’accolades crée un dictionnaire
vide : "{}". Placer une liste de paires clé:valeur séparées par des
virgules à l’intérieur des accolades ajoute les valeurs
correspondantes au dictionnaire ; c’est également de cette façon que
les dictionnaires sont affichés.

Les opérations classiques sur un dictionnaire consistent à stocker une
valeur pour une clé et à extraire la valeur correspondant à une clé.
Il est également possible de supprimer une paire clé:valeur avec
"del". Si vous stockez une valeur pour une clé qui est déjà utilisée,
l’ancienne valeur associée à cette clé est perdue. Si vous tentez
d’extraire une valeur associée à une clé qui n’existe pas, une
exception est levée.

La méthode "keys()" d’un dictionnaire retourne une liste de toutes les
clés utilisées dans le dictionnaire, dans un ordre arbitraire (si vous
voulez qu’elle soit triée, appliquez-lui la fonction "sorted()"). Pour
tester si une clé est dans le dictionnaire, utilisez le mot-clé "in".

Voici un petit exemple utilisant un dictionnaire :

   >>> tel = {'jack': 4098, 'sape': 4139}
   >>> tel['guido'] = 4127
   >>> tel
   {'sape': 4139, 'guido': 4127, 'jack': 4098}
   >>> tel['jack']
   4098
   >>> del tel['sape']
   >>> tel['irv'] = 4127
   >>> tel
   {'guido': 4127, 'irv': 4127, 'jack': 4098}
   >>> tel.keys()
   ['guido', 'irv', 'jack']
   >>> 'guido' in tel
   True

Le constructeur "dict()" fabrique un dictionnaire directement à partir
d’une liste de paires clé-valeur stockées sous la forme de tuples :

   >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
   {'sape': 4139, 'jack': 4098, 'guido': 4127}

De plus, il est possible de créer des dictionnaires par compréhension
depuis un jeu de clef et valeurs :

   >>> {x: x**2 for x in (2, 4, 6)}
   {2: 4, 4: 16, 6: 36}

Lorsque les clés sont de simples chaînes de caractères, il est parfois
plus facile de spécifier les paires en utilisant des paramètres nommés
:

   >>> dict(sape=4139, guido=4127, jack=4098)
   {'sape': 4139, 'jack': 4098, 'guido': 4127}


5.6. Techniques de boucles
==========================

Lorsque vous itérez sur une séquence, la position et la valeur
correspondante peuvent être récupérées en même temps en utilisant la
fonction "enumerate()". :

   >>> for i, v in enumerate(['tic', 'tac', 'toe']):
   ...     print i, v
   ...
   0 tic
   1 tac
   2 toe

Pour faire des boucles sur deux séquences ou plus en même temps, les
éléments peuvent être associés par la fonction "zip()"

   >>> questions = ['name', 'quest', 'favorite color']
   >>> answers = ['lancelot', 'the holy grail', 'blue']
   >>> for q, a in zip(questions, answers):
   ...     print 'What is your {0}?  It is {1}.'.format(q, a)
   ...
   What is your name?  It is lancelot.
   What is your quest?  It is the holy grail.
   What is your favorite color?  It is blue.

Pour faire une boucle en sens inverse sur une séquence, commencez par
spécifier la séquence dans son ordre normal, puis appliquez la
fonction "reversed()"

   >>> for i in reversed(xrange(1,10,2)):
   ...     print i
   ...
   9
   7
   5
   3
   1

Pour faire une boucle selon un certain classement sur une séquence,
utilisez la fonction "sorted()", elle renvoie une nouvelle liste
classée sans altérer la source :

   >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
   >>> for f in sorted(set(basket)):
   ...     print f
   ...
   apple
   banana
   orange
   pear

Lorsque vous faites une boucle sur un dictionnaire, les clés et leurs
valeurs peuvent être récupérées en même temps en utilisant la méthode
"iteritems()"

   >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
   >>> for k, v in knights.iteritems():
   ...     print k, v
   ...
   gallahad the pure
   robin the brave

Il est parfois tentant de modifier une liste pendant son itération.
Cependant, c’est souvent plus simple et plus sûr de créer une nouvelle
liste à la place.

   >>> import math
   >>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
   >>> filtered_data = []
   >>> for value in raw_data:
   ...     if not math.isnan(value):
   ...         filtered_data.append(value)
   ...
   >>> filtered_data
   [56.2, 51.7, 55.3, 52.5, 47.8]


5.7. Plus d’informations sur les conditions
===========================================

Les conditions utilisées dans une instruction "while" ou "if" peuvent
contenir n’importe quel opérateur, pas seulement des comparaisons.

Les opérateurs de comparaison "in" et "not in" testent si une valeur
est présente ou non dans une séquence. Les opérateurs "is" et "is not"
testent si deux objets sont vraiment le même objet ; ceci n’est
important que pour des objets muables comme des listes. Tous les
opérateurs de comparaison ont la même priorité, qui est plus faible
que celle des opérateurs numériques.

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

Les comparaisons peuvent être combinées en utilisant les opérateurs
booléens "and" et "or", le résultat d’une comparaison (ou de toute
expression booléenne) pouvant être inversé avec "not". Ces opérateurs
ont une priorité inférieure à celle des opérateurs de comparaison ;
entre eux, "not" a la priorité la plus élevée et "or" la plus faible,
de telle sorte que "A and not B or C" est équivalent à "(A and (not
B)) or C". Comme toujours, des parenthèses peuvent être utilisées pour
exprimer l’instruction désirée.

Les opérateurs booléens "and" et "or" sont appelés opérateurs *en
circuit court* : leurs arguments sont évalués de la gauche vers la
droite et l’évaluation s’arrête dès que le résultat est déterminé. Par
exemple, si "A" et "C" sont vrais et "B" est faux, "A and B and C"
n’évalue pas l’expression "C". Lorsqu’elle est utilisée en tant que
valeur et non en tant que booléen, la valeur de retour d’un opérateur
en circuit court est celle du dernier argument évalué.

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

   >>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
   >>> non_null = string1 or string2 or string3
   >>> non_null
   'Trondheim'

Notez qu’en Python, à la différence du C, des affectations ne peuvent
pas intervenir à l’intérieur d’expressions. Les programmeurs C
râleront peut-être après cela, mais cela évite des erreurs fréquentes
que l’on rencontre en C, lorsque l’on tape "=" alors que l’on voulait
faire un test avec "==".


5.8. Comparer des séquences avec d’autres types
===============================================

Des séquences peuvent être comparées avec d’autres séquences du même
type. La comparaison utilise un ordre *lexicographique* : les deux
premiers éléments de chaque séquence sont 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 à leur tour, et ainsi de suite
jusqu’à ce que l’une des séquences soit épuisée. Si deux éléments à
comparer sont eux-mêmes des séquences du même type, alors la
comparaison lexicographique est effectuée récursivement. Si tous les
éléments des deux séquences sont égaux, les deux séquences sont alors
considérées comme égales. Si une séquence est une sous-séquence de
l’autre, la séquence la plus courte est celle dont la valeur est
inférieure. La comparaison lexicographique des chaînes de caractères
utilise l’ordre ASCII des caractères. Voici quelques exemples de
comparaisons entre séquences de même type :

   (1, 2, 3)              < (1, 2, 4)
   [1, 2, 3]              < [1, 2, 4]
   'ABC' < 'C' < 'Pascal' < 'Python'
   (1, 2, 3, 4)           < (1, 2, 4)
   (1, 2)                 < (1, 2, -1)
   (1, 2, 3)             == (1.0, 2.0, 3.0)
   (1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

La comparaison d’objets de types différents est autorisée. Le résultat
est déterminé mais arbitraire : les types sont ordonnés par leur nom.
Ainsi, une liste (type "list") est toujours inférieure à une chaîne de
caractères (type "str"), une chaîne de caractères toujours inférieure
à un tuple… [1] Des types numériques mélangés sont comparés en
fonction de leur valeur numérique, donc 0 est égal à 0.0, etc.

-[ Notes ]-

[1] Les règles de comparaison d’objets de types différents ne
    doivent pas être considérées comme fiables ; elles peuvent être
    amenées à changer dans une future version du langage.
