Vendredi 1er septembre 2017
Dans python, ma fonction préférée est yield
. Comme moi, c'est une feignasse. Elle attend qu'on l'invoque, brandit son résultat et retourne dans son terrier.
Avec yield
on pourrait redéfinir la fonction range ainsi :
def range(begin, end, step):
x = begin
while x < end:
yield x
x += step
Mais range
ne produit que des séquences de nombres entiers. Alors que notre fonction ci-dessus peut aussi bien accepter des paramètres flottants(float
) et retourner des séquences de flottants.
def frange(begin, end, step):
x = begin
while x < end:
yield x
x += step
Exemple :
>>> l = list(range(1,.01))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer
>>> l = frange(0, 1, 0.1)
>>> print([round(x, 1) for x in l])
[0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
Note : le manège avec round(x, 1) for ...
est là pour éviter l'affichage avec un paquet de décimales
Il arrive qu'on ait besoin d'extraire les éléments d'une liste deux par deux.
Exemple : la méthode create_line de la classe Canvas de tkinter, admet un nombre indéfini de paramètres x1, y1, x2, y2, ...
. Sa signature est :
def create_line(self, *args, **kw)
On voudrait faire un traitement aux coordonnées des points avant l'appel de la fonction.
Notre fonction génératrice pourrait être définie par :
def couple(l):
it = iter(l)
while True: yield next(it), next(it)
A chaque appel de couple, yield
founit un couple. On pourrait alors écrire :
>>> l = list(range(8))
>>> print([c for c in couple(l)])
[(0, 1), (2, 3), (4, 5), (6, 7)]
>>>
Remarque
On pouvait utilser zip
et les tranches de liste [start:stop:step]
plus exactement l[0::1]
et l[1::2]
>>> [c for c in zip(l[0::2], l[1::2])]
[(0, 1), (2, 3), (4, 5), (6, 7)]
L'avantage des générateurs c'est leur caractère fainéant. Un autre avantage, on n'a pas besoin d'aplatir les tuples.
Remarque
Subtilité de la langue françoise : on écrit «fainéant» mais «feignasse».