2009-10-13 8 views
13

Je cherche un moyen d'inverser un objet générateur. Je sais comment inverser les séquences:Python Reverse Generator

foo = imap(seq.__getitem__, xrange(len(seq)-1, -1, -1)) 

Mais est quelque chose de similaire possible avec un générateur que l'entrée et un générateur inversé comme la sortie (len (seq) reste le même, la valeur de la séquence d'origine peut être utilisé)?

+3

Je dois prendre l'exception avec votre exemple d'inverser une séquence. Pourquoi ne pas simplement utiliser 'reverse'? ou '.reverse'? Même 'seq [:: - 1]' est plus clair que ce que vous avez écrit. –

+0

Parce que tous ces exemples vont créer une nouvelle liste. Mon exemple ci-dessus est la seule façon que je connaisse de créer une liste sans la copier d'abord. –

+0

Eh bien, j'ai appris quelque chose de nouveau - seq [:: - 1] * fait * en fait créer une nouvelle liste. Voir ma réponse d'expression de générateur pour une alternative en utilisant des indices négatifs. – PaulMcG

Répondre

19

Vous ne pouvez pas inverser un générateur de manière générique, sauf en le transformant en une séquence et en créant un itérateur à partir de cela. Les termes ultérieurs d'un générateur ne peuvent pas nécessairement être connus avant que les termes antérieurs aient été calculés.

Pire encore, vous ne pouvez pas savoir si votre générateur atteindra jamais une exception StopIteration tant que vous ne l'avez pas touché, il n'y a donc aucun moyen de savoir quel sera le premier terme de votre séquence.

Le mieux que vous pourriez faire serait d'écrire une fonction reversed_iterator:

def reversed_iterator(iter): 
    return reversed(list(iter)) 

EDIT: Vous pouvez aussi, bien sûr, remplaçons inversé dans ce avec votre imap base version itérative, pour sauver une création de la liste.

+13

Strictement parlant, 'list (iter)' n'est pas un * cast *, c'est la construction d'une liste en consommant l'iterator iter. Je ne suis pas sûr que Python ait des fonctions * cast *, du moins pas dans le sens où ce terme est utilisé dans des langages comme C ou Java. int ("100") n'est pas un cast, et float (100) n'est pas un cast - les deux sont des constructeurs qui retournent des objets. En C, si quelque chose est lancé, l'objet original reste le même. Si vous avez fait une telle chose en Python, vous pourriez prendre l'id de la valeur d'origine, et la valeur casted, et ils seraient les mêmes. En bref: en Python, le casting est mort. – PaulMcG

+5

"La distribution est morte": bon! :) –

+2

@ Paul McGuire: Je suis d'accord avec les parties importantes de ce que vous avez dit. Un seul, cependant: en C, un cast * peut * changer l'objet original. Si vous lancez 100 pour flotter, C le changera en 100.0f; C ne jettera pas simplement le modèle de bits 0x00000064 dans la variable float et le laissera se transformer en n'importe quelle valeur qui serait dans un flottant.En C, un cast peut simplement changer de type (changer 'int '' '' long int * ', ou changer' int' en 'long int') ou changer de valeur et de type. C++ a plusieurs opérateurs de casting, dont un qui "réinterprète" le type sans changer la valeur du tout. – steveha

6

reversed(list(input_generator)) est probablement le moyen le plus simple.

Il est impossible d'obtenir les valeurs d'un générateur dans l'ordre «inverse» sans les regrouper toutes en une séquence, car la génération du second élément peut très bien s'appuyer sur la génération du premier.

4

Vous devez traverser le générateur de toute façon pour obtenir le premier élément, donc vous pourriez aussi bien faire une liste. Essayez

reversed(list(g)) 

g est un générateur.

reversed(tuple(g)) 

fonctionnerait aussi bien (je n'ai pas vérifié pour voir s'il y a une différence significative de la performance).

Questions connexes