Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

"Longueur" d'un itérateur

3 réponses
Avatar
Pierre Maurette
Bonjour,

Le titre est trompeur, j'ai bien vu qu'un itérateur ou un générateur
n'a pas de len(), que son nombre d'éléments peut être infini, et que
sauf erreur de ma part s'il ne l'est pas, on ne le connaitra qu'après
la consommation complète de l'itérateur.
D'un autre coté, connaitre cette longueur avant de commencer à
consommer l'itérateur peut être intéressant. Je parle ici de la
longueur initiale, pas de la longueur restante ou quoi que ce soit
d'autre.
J'ai fait comme ça:

class getFileListI(object):

def __init__(self, options):
# entre autres choses fabrique self.options
# à partir de options.

def __len__(self):
try:
return self.longueur
except AttributeError:
self.longueur = sum(1 for _1 in getFileListI(self.options))
return self.longueur

Ça me permet d'utiliser len() sur l'objet getFileListI, même
éventuellement après ou pendant sa consommation, tout en ne créant et
parcourant le temporaire au pire qu'une fois:

it = getFileListI(options)
print len(it)
for item in it:
#faire quelque chose
print len(it)

Y a-t-il d'autres solutions, en particulier qui éviteraient la création
et le parcours du temporaire ?

Question de style également: je suis à priori le seul utilisateur de
mes outils. Malgré tout, j'aime bien les laisser à peu près terminés et
dans un état le plus possible conforme aux bonnes moeurs pythoniennes.
Est-ce que le fait de créer un len() sur un itérateur entre dans ces
bonnes moeurs ? Puisqu'àprès tout je ne gagne rien en performances par
rapport au traitement du problème par l'appelant, qui au moins sait ce
qu'il fait:

longueur = sum(1 for _1 in getFileListI(options))
it = getFileListI(options)
print longueur
for item in it:
#faire quelque chose
print longueur

J'ai également une fonction qui me renvoie une liste, sur laquelle on
peut utiliser len():

def getFileList(options):
"""Renvoie une liste à partir de getFileListI(options)"""
return list(getFileListI(options))


Bonne journée...

--
Pierre Maurette

3 réponses

Avatar
Amaury Forgeot d'Arc
Le 22/01/2010 10:38, Pierre Maurette a écrit :
Bonjour,

Le titre est trompeur, j'ai bien vu qu'un itérateur ou un générateur n'a
pas de len(), que son nombre d'éléments peut être infini, et que sauf
erreur de ma part s'il ne l'est pas, on ne le connaitra qu'après la
consommation complète de l'itérateur.
D'un autre coté, connaitre cette longueur avant de commencer à consommer
l'itérateur peut être intéressant. Je parle ici de la longueur initiale,
pas de la longueur restante ou quoi que ce soit d'autre.


[...]

Il y a la méthode non documentée __length_hint__(),
que python lui-même implémente dans ses propres itérateurs,
et qu'il utilise dans certaines fonctions qui
prennent un iterable (tuple(), list.extend(), map(), zip()...)
en vue d'optimiser l'allocation.

Mais implémenter une fonction non documentée n'est sans doute pas
très propre...

--
Amaury Forgeot d'Arc
Avatar
Pierre Maurette
Amaury Forgeot d'Arc, le 24/01/2010 a écrit :

[...]

Il y a la méthode non documentée __length_hint__(),
que python lui-même implémente dans ses propres itérateurs,
et qu'il utilise dans certaines fonctions qui
prennent un iterable (tuple(), list.extend(), map(), zip()...)
en vue d'optimiser l'allocation.

Mais implémenter une fonction non documentée n'est sans doute pas
très propre...



Merci. Cette méthode semble renvoyer le nombre d'éléments restant à
consommer dans l'itérateur, c'est à dire le nombre d'éléments dans
l'itérateur à son état actuel:

it = iter(range(1, 20, 3))
print 'n%s' % it.__length_hint__()
for _1 in it:
print '%d/%d' % (_1, it.__length_hint__()),
print 'n%s' % it.__length_hint__()

7
1/6 4/5 7/4 10/3 13/2 16/1 19/0
0

Mais:

li = range(1, 20, 3)
it = iter(li)
print 'n%s' % it.__length_hint__()
for _1 in it:
if len(li) < 15:
li.append(5*_1)
print '%d/%d' % (_1, it.__length_hint__()),
print 'n%s' % it.__length_hint__()

7
1/7 4/7 7/7 10/7 13/7 16/7 19/7 5/7 20/6 35/5 50/4 65/3 80/2 95/1 25/0
0

ce qui reste logique, bien que potentiellement piégeux.

Pour ce qui est de ma question, je laisse tomber le len(). Déjà, je
pense que c'est carrément sale d'implémenter len() sur un itérateur.
Ensuite mon implémentation était boguée. La valeur renvoyée pouvait
dépendre du moment de l'appel, puisque je travaille sur une
arborescence qui peut être modifiée pendant le parcours de l'itérateur,
c'est même un peu le but de la manip.

Bonne journée

--
Pierre Maurette
Avatar
Bruno Desthuilliers
Pierre Maurette a écrit :
(snip)
>
Pour ce qui est de ma question, je laisse tomber le len(). Déjà, je
pense que c'est carrément sale d'implémenter len() sur un itérateur.



Bin, c'est un peu l'avis que j'allais te donner, mais je vois que tu es
arrivé tout seul à cette conclusion...