Générateurs & itérateurs#
avancé
Protocole d'itération#
Un objet itérable implémente __iter__ (renvoie un itérateur). Un itérateur implémente __iter__ et __next__ (lève StopIteration en fin de séquence).
Fonction génératrice (yield)#
def compteur(debut, fin):
n = debut
while n < fin:
yield n
n += 1
for x in compteur(0, 3): # 0 1 2
print(x)
g = compteur(0, 3)
next(g) # 1er appel : exécute jusqu'au premier yield
Chaque appel à next() reprend l'exécution juste après le yield précédent (état conservé entre les appels).
Expression génératrice#
yield from#
def chaine(*iterables):
for it in iterables:
yield from it
list(chaine([1, 2], (3, 4), "ab")) # [1, 2, 3, 4, 'a', 'b']
Pourquoi utiliser un générateur#
- Traiter des séquences volumineuses (voire infinies) sans tout charger en mémoire.
- Pipeline de transformations paresseuses (
map,filter, générateurs enchaînés).
def lire(chemin):
with open(chemin, encoding="utf-8") as f: # le fichier reste ouvert le temps du parcours
for ligne in f:
yield ligne.strip()
non_vides = (l for l in lire("gros_fichier.txt") if l)
majuscules = (l.upper() for l in non_vides) # rien n'est lu tant qu'on n'itère pas
Itérateur personnalisé (classe)#
class Compteur:
def __init__(self, debut, fin):
self.n = debut
self.fin = fin
def __iter__(self):
return self
def __next__(self):
if self.n >= self.fin:
raise StopIteration
valeur = self.n
self.n += 1
return valeur
itertools (aperçu)#
from itertools import chain, islice, count, cycle, zip_longest, groupby, product, combinations
list(chain([1, 2], [3, 4])) # [1, 2, 3, 4] — concatène des itérables
list(islice(count(10), 3)) # [10, 11, 12] — tranche d'un itérable infini
list(zip_longest([1, 2, 3], "ab", fillvalue=None)) # [(1,'a'), (2,'b'), (3,None)]
list(product([0, 1], repeat=2)) # [(0,0), (0,1), (1,0), (1,1)]
list(combinations("abc", 2)) # [('a','b'), ('a','c'), ('b','c')]
# groupby : regroupe les éléments CONSÉCUTIFS égaux -> trier d'abord sur la même clé
donnees = sorted([("A", 1), ("B", 2), ("A", 3)], key=lambda t: t[0])
{cle: [v for _, v in grp] for cle, grp in groupby(donnees, key=lambda t: t[0])}
# {'A': [1, 3], 'B': [2]}