Nov 21, 2024

Wiki

Python

Aide

edit SideBar

Search

Itertools


Le module itertools offre plusieurs outils pour une utilisation avancée des generators...

Préliminaires

Un compteur

Le module itertools fournit un itérateur count contenant un compteur, qui s'incrémente à chaque appel, en commençant

  • par l'argument spécifié,
  • par 0, en l'absence d'argument.
  >>> from itertools import count
>>> icpt = count(5)
  >>> for k in range(7):
  ...     print icpt.next(), 
  ... 
  5 6 7 8 9 10 11

Exemple de base

Afin de pouvoir illustrer les outils du module itertools, on suppose avoir créé deux generators, l'un pour la suite de Fibonacci, l'autre pour les nombres premiers...

  >>> def fibonacci():
  ...     a, b = 0, 1
  ...     while True:
  ...         yield b
  ...         a, b = b, a+b
  ... 
  >>> suite = fibonacci()
  >>> def nombre_premier():
  ...     nombre = 2
  ...     premiers = [2]
  ...     yield nombre
  ...     while True:
  ...         nombre += 1
  ...         if len([k for k in premiers if nombre%k == 0]) == 0:
  ...             yield nombre
  ...             premiers.append(nombre)
  ... 
  >>> premier = nombre_premier()

Opérations sur un unique generator

Les cycles

Considérons le generator suivant...

  >>> def gen():
  ...    yield 1
  ...    yield 2
  ... 

Au premier appel, on obtiendra 1, puis 2, et enfin une exception de type StopIteration...

  >>> unGen = gen()
  >>> unGen.next()  
  1
  >>> unGen.next() 
  2
  >>> unGen.next()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  StopIteration

Si l'on souhaite plutôt boucler, obtenir un cycle (après 2, recommencer à 1), on peut utiliser l'outil cycle de itertools...

  >>> unGen = gen()
  >>> from itertools import cycle
  >>> unCycle = cycle(unGen)
  >>> unCycle.next()
  1
  >>> unCycle.next() 
  2
  >>> unCycle.next()
  1

ifilter

ifilter(fun, gen) permet de ne conserver que les éléments $x$ du generator $gen$ tels que $fun(x)$ est True

Ainsi, si l'on souhaite obtenir le generator des nombres premiers de la forme : un carré plus 1 ($5 = 2^2 + 1$, $17 = 4^2 + 1$), on peut

  • créer une fonction qui teste si un nombre est de la forme : un carré plus 1,
  • créer le generator des nombres premiers, ce qui a été fait plus haut,
  • utiliser enfin ifilter...
  >>> from math import sqrt
>>> def est_carre_plus_un(x):
  ...     return int(sqrt(float(x)-1))**2+1 == x
  ...
  >>> premiers = nombre_premier()
  >>> from itertools import ifilter
  >>> premiers_formes = ifilter(est_carre_plus_un, premiers)
  >>> for k in range(10):
  ...     print premiers_formes.next(),
  ... 
  2 5 17 37 101 197 257 401 577 677

Il existe aussi une méthode ifilterfalse, dont l'explication est immédiate.

Takewhile

takewhile(fun, seq) renvoie les éléments de seq tant que fun(seq(n)) renvoie True.

Par exemple, si l'on veut obtenir les nombres de Fibonacci inférieurs à 100, on peut

  • créer une fonction qui teste si un nombre est inférieur à 100,
  • utiliser un generator de nombres de Fibonacci,
  • enfin, utiliser takewhile...
  >>> def est_inferieur(x):
... return x<100
  ... 
  >>> from itertools import takewhile
  >>> fibos = fibonacci()
  >>> cent_fibos = takewhile(est_inferieur, fibos)
  >>> while True:
  ...     print cent_fibos.next(),
  ... 
  1 1 2 3 5 8 13 21 34 55 89
  Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
  StopIteration

Dropwhile

takewhile(fun, seq) renvoie les éléments de seq dès que fun(seq(n)) renvoie faux.

Par exemple, pour obtenir la suite nombres de fibonacci supérieurs à 100, il suffit de reprendre le code ci-dessus, et de remplacer takewhile par dropwhile :

  >>> fibos = fibonacci()
  >>> pas_cent_fibos = dropwhile(est_inferieur, fibos)
  >>> for k in range(10):
  ...     print pas_cent_fibos.next(),
  ... 
  144 233 377 610 987 1597 2584 4181 6765 10946

Islice

Extrait des tranches d'itérateurs...

islice(gen, n)
Itère gen sur les n premiers termes de l'itérateur.
Par exemple, pour obtenir les 20 premiers nombres premiers, on utilisera islice(gen, 20)...
  >>> iprime = nombre_premier()
  >>> from itertools import islice
  >>> iprime100prem = islice(iprime,20)
  >>> while True:
  ...     print iprime100prem.next(),
  ... 
  2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71
  Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
  StopIteration
islice(gen, a, b)
Itère gen du aième terme de l'itérateur, jusqu'à son bième terme (exclu).
Par exemple, pour obtenir le millième nombre premier, et ses 10 successeurs, on utilisera islice(gen, 1000, 1010)...
  >>> iprime = nombre_premier()
  >>> from itertools import islice
  >>> iprime1000 = islice(iprime, 1000, 1010)
  >>> while True:
  ...     print iprime1000.next(),
  ... 
  8117 8123 8147 8161 8167 8171 8179 8191 8209 8219
islice(gen, a, b, pas)
Comme ci-dessus, mais en précisant un pas.

Opérations sur plusieurs generators

Les chaînes

La fonction chain de itertools permet de concaténer plusieurs generators : quand on arrive à la fin d'un generator, on passe au suivant.

  >>> def gen1():
  ...     yield 1
  ...     yield 2
  ... 
  >>> def gen2():
  ...     yield 'a'
  ...     yield 'b'
  ... 
  >>> from itertools import chain
  >>> unGen1 = gen1()
  >>> unGen2 = gen2()
  >>> une_chaine = chain(unGen1, unGen2)
  >>> while True:
  ...    une_chaine.next(), 
  ... 
  1 2 'a' 'b'
  Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
  StopIteration
  >>> 

izip

La fonction izip(seq1, seq2) du module itertools permet de renvoyer le generator constitué des couples (seq1[k], seq2[k]) :

  >>> from itertools import izip
  >>> couple = izip(suite, premier)
  >>> for k in range(10):
  ...     print couple.next(),
  ... 
  (1, 2) (1, 3) (2, 5) (3, 7) (5, 11) (8, 13) (13, 17) (21, 19)
  (34, 23) (55, 29)
  >>> 

Le generator défini ci-dessous renvoie effectivement un couple constitué du nième terme de la suite de Fibonacci et du nième nombre premier.

Si l'on passe N generators à izip, alors la même chose se produit : un generator de N-uples est créé.

imap

imap(fun, seq1, seq2) renvoie le generator dont les termes sont (fun(seq1[k]), fun(seq2[k]))

  >>> def carre(x,y):
  ...     return x**2, y**2
  ... 
  >>> from itertools import imap
  >>> couple = imap(carre, suite, premier)
  >>> for k in range(10):
  ...     print couple.next(),
  ... 
  (1, 4) (1, 9) (4, 25) (9, 49) (25, 121) (64, 169) (169, 289) 
  (441, 361) (1156, 529) (3025, 841)

On récupère de cette manière la liste des carrés des couples Fibonacci, nombres premiers.

Une fois encore, imap peut accepter un nombre quelconque de generators en entrée.

Page Actions

Recent Changes

Group & Page

Back Links