10
votes

Comment compter les éléments d'un générateur consommé par autre code

Je crée un générateur qui est consommé par une autre fonction, mais j'aimerais toujours savoir combien d'articles ont été générés: xxx pré>

Le meilleur que je puisse arriver, c'est de envelopper le générateur avec une classe qui conserve un compte, ou peut-être le tourner à l'intérieur et envoyez-le () les choses dans. Y a-t-il un moyen élégant et efficace de voir combien d'articles un générateur produit lorsque vous n'êtes pas celui qui ne l'attend pas en python 2? P>

EDIT: STRUT> Voici ce que j'ai fini par: P>

class Count(Iterable):
    """Wrap an iterable (typically a generator) and provide a ``count``
    field counting the number of items.

    Accessing the ``count`` field before iteration is finished will
    invalidate the count.
    """
    def __init__(self, iterable):
        self._iterable = iterable
        self._counter = itertools.count()

    def __iter__(self):
        return itertools.imap(operator.itemgetter(0), itertools.izip(self._iterable, self._counter))

    @property
    def count(self):
        self._counter = itertools.repeat(self._counter.next())
        return self._counter.next()


1 commentaires

Lorsque vous dites que vous créez le générateur, vous voulez dire que vous définissez une fonction à l'aide d'un rendement ou de créer un objet générateur d'une compréhension ou similaire?


7 Réponses :


8
votes

Habituellement, je voudrais juste tourner le générateur dans une liste et prendre sa longueur. Si vous avez des raisons de supposer que cela consommera trop de mémoire, votre meilleur choix semble en effet être la classe d'emballage que vous avez suggérée vous-même. Ce n'est pas trop mauvais, bien que: xxx

(la dernière ligne est pour la compatibilité avant de Python 3.x.)


0 commentaires

10
votes

Voici un autre moyen d'utiliser itTools.count () Exemple: XXX

J'espère que c'était utile.


6 commentaires

Vous avancez le compteur juste pour le lire, alors si vous le faites deux fois, ce n'est pas faux. Aussi ithertool.izip (générateur (), compteur) est juste (renversé) énumérable


Vous devez supprimer le - 1 lors de la lecture du compteur. Votre exemple doit réellement imprimer 6 , car processus () consomme les éléments 0, 1, 2, 3, 4, 5 .


@Jochen ritzel: désolé mais je ne comprends pas votre commentaire sur le faire deux fois ?, Et je ne vois pas comment énumérer () peut obtenir le même résultat parce que le L'idée est de lire le résultat du compteur après, désolé mais je ne vois pas comment vous allez le faire (propre) à l'aide de énumérer () mais je suis d'accord si vous dites que zip () est identique à ithertool.izip () dans ce cas.


@Jochen Ritzel: J'ai ajouté une autre façon d'obtenir la valeur de contre-valeur sans l'avancer, je ne l'aimais pas aussi bien en premier lieu et je ne sais pas si cela résolvez le problème que vous avez vu :).


C'est ce que je cherchais (Eh bien, avant votre REP () Hack, de toute façon.;). Pour une bonté d'iTERTools encore plus gracieuse, vous pouvez faire ithertools.imap (opérateur.Itemplgetter (0), ithertool.izip (itéroureux, compteur)) .


@Jay: Ce n'est pas que le mal utiliser le REC () pour obtenir la contre-valeur, je le trouve plus épicé :) mais bon.



2
votes

Voici une autre approche. L'utilisation d'une liste pour la sortie de comptage est un peu laide, mais c'est assez compact: xxx

utilisé comme: xxx

un pourrait alternativement rendre compteur () prendre un dict dans lequel il peut ajouter une touche "Compt" ou un objet sur lequel il pourrait définir un membre compter .


0 commentaires

14
votes

Si vous ne vous souciez pas de consommer le générateur, vous pouvez simplement faire:

sum(1 for x in gen)


0 commentaires

1
votes

Ceci est une autre solution similaire à celle de @ Sven-Marnach: xxx

J'ai enveloppé l'itérateur avec une fonction de générateur et évité à nouveau définir suivant . Le résultat est itérable (pas un itérateur car il manque Suivant méthode), mais si c'est EUUGH, il est plus rapide. Dans mes tests, cela représente 10% plus rapide.


0 commentaires

2
votes

Si vous n'avez pas besoin de renvoyer le nombre et de vouloir simplement le connecter, vous pouvez utiliser un bloc enfin:

10 iterations
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


0 commentaires

0
votes

Cette solution utilise SIDE_EFFECT CODE> dans le package More_iterTools CODE>.

>>> iterator, counter = counter_wrap((1, 2, 3, 4, 5, 6, "plast", "last"))
>>> counter()
0
>>> counter()  # Calling this has no side effect (counter not incremented)
0
>>> next(iterator)
1
>>> next(iterator)
2
>>> next(iterator)
3
>>> counter()  # Updates when the iterator returns an element
3
>>> next(iterator)
4
>>> next(iterator)
5
>>> next(iterator)
6
>>> next(iterator)
'plast'
>>> counter()
7
>>> next(iterator)
'last'
>>> counter()
8
>>> next(iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> counter()
8


0 commentaires