J'ai le map bien trop bas, j'ai l'filter qui s'dilate...
Ca fait maintenant plusieurs années que Guido parle de supprimer
map(),
filter(),
reduce(), etc.. du langage, ce qui
arrivera un jour puisque 50% de la communauté est en faveur de ce retrait,
25% contre et 25% sans opinions.
lambda suivra probablement même
chemin.
Mais que reste-t-il aux développeurs qui usent et abusent de ces primitives
pour faire de la programmation fonctionnelle ?
... roulement de tambours ...
itertools ! qui offre des utilitaires de création
d'itérateurs.
Iterators ? Generators ? Yield ?
itertools regroupe des fonctions de génération d'
itérateurs, ces
objets renvoyés par des
generators, basés sur la directive
yield, qui permet de sauvegarder des états d'exécution de la
fonction.
-> Plus
d'infos sur yield dans le manuel de référence
Un exemple typique de l'utilisation des generators, est l'implémentation de
la suite de Fibonnaci:
def fibonnaci():
x = 0
y = 1
while 1:
x, y = y, x + y
yield x
Cette fonction renvoie un itérateur sans fin, qui permet de parcourir la
suite:
>>> def fibonnaci():
... x = 0
... y = 1
... while 1:
... x, y = y, x + y
... yield x
...
>>> my_fib = fibonnaci()
>>> my_fib.next()
1
>>> my_fib.next()
1
>>> my_fib.next()
2
>>> my_fib.next()
3
Ce principe peut être utilisé pour implémenter tout type de besoin, et à
fortiori remplacer
map(),
filter(),
reduce() et
zip().
Contenu d'itertools
- chain(*iterables): renvoie un iterator composé de tous les
éléments fournis dans les
iterables.
- count([firstval]): Permet de retourner un iterator qui renvoie
des entiers incrémentés par pas de 1. Si firstval est fourni, il est le
premier entier renvoyé. Sinon count() utilise
0.
- cycle(iterable): Renvoie un iterator qui permet de parcourir
indéfiniment les éléments de
l'itérable.
- groupby(iterable[, keyfunc]): Renvoie un iterator qui permet de
récupérer des couples (clé, groupe). keyfunc est une fonction qui doit
renvoyer la clé pour l'élément courant. groupe est un itérable qui réunit
les éléments regroupés par clé.
- ifilter(predicate, iterable): Permet de renvoyer un iterator qui
contient les éléments de l'itérable fourni, lorsque le callable predicate
renvoie vrai. Si predicate vaut None, les valeurs sont testées avec
bool().
- imap(function, *iterables): Renvoie un iterator qui appelle
function avec les éléments des itérables fournis, concaténés pour former la
liste des paramètres. Si function vaut None, renvoie les paramètres
préparés.
- islice(iterable, [start,] stop [, step]): Permet de renvoyer un
itérable qui est une sous-séquence de l'itérable fourni. start, stop et
step s'utilisent comme les tranches.
- izip(*iterables): Fonctionne comme zip(), pour agréger les
éléments des itérables fournis.
- repeat(element, times): Génère un iterator qui répète element
times fois. Si times n'est pas fourni, devient un iterator infini qui
renvoie toujours element.
- starmap(function, sequence): Comparable à imap() mais le
deuxième argument doit être une séquence de tuples. À chaque itération n,
l'iterator renvoie le résultat de function(*sequence[n]).
- takewhile(predicate, iterable): Renvoie les éléments de iterable
tant que predicate(element) renvoie True.
- tee(iterable[, n=2]): Découpe iterable en n itérables, renvoyés
sous la forme d'un tuple. Chaque itérable renvoie ensuite les éléments de
iterable.
Toutes ces fonctions peuvent être combinées, pour obtenir des mécanismes
très puissants.
A chaque fois que vous vous apprêter à ajouter un appel à
map(),
zip() et consort, allez plutôt faire un tour du coté
d'itertools...