web-dev-qa-db-fra.com

Obtenir le nième élément d'un générateur en Python

Existe-t-il une manière plus concise syntaxiquement d’écrire ce qui suit?

gen = (i for i in xrange(10))
index = 5
for i, v in enumerate(gen):
    if i is index:
        return v

Il semble presque naturel qu'un générateur ait une expression gen[index], qui agit comme une liste, mais qui est fonctionnellement identique au code ci-dessus.

46
Oliver Zheng

une méthode consisterait à utiliser itertools.islice

>>> next(itertools.islice(xrange(10), 5, 5 + 1))
5
45
cobbal

Vous pouvez le faire en utilisant count comme exemple de générateur:

from itertools import islice, count
next(islice(count(), n, n+1))
15
Mark Byers

Je pense que le meilleur moyen est: 

next(x for i,x in enumerate(it) if i==n)

(où it est votre itérateur et n est l'index)

Cela ne vous oblige pas à ajouter une importation (comme les solutions utilisant itertools) ni à charger simultanément tous les éléments de l'itérateur en mémoire (comme les solutions à l'aide de list).

Remarque 1: cette version génère une erreur StopIteration si votre itérateur contient moins de n éléments. Si vous voulez obtenir None à la place, vous pouvez utiliser:

next((x for i,x in enumerate(it) if i==n), None)

Remarque 2: Il n'y a pas de crochets dans l'appel à next. Ce n’est pas une compréhension de liste, mais une compréhension de générateur, qui ne consomme pas l’itérateur original plus loin que son nième élément.

1
lovasoa

Le mieux est d'utiliser: Exemple: 

a = gen values ('a','c','d','e')

alors la réponse sera: 

a = list(a) -> this will convert the generator to a list (it will store in memory)

alors quand vous voulez aller index spécifique vous allez: 

a[INDEX] -> and you will able to get the value its holds 

si vous souhaitez connaître uniquement le nombre ou effectuer des opérations qui ne nécessitent pas de stockage en mémoire, la meilleure pratique consiste à: a = sum(1 in i in a) -> ceci comptera le nombre d'objets que vous avez.

j'espère que je l'ai fait plus simple. 

1
Ido Bleicher

Je m'opposerais à la tentation de traiter les générateurs comme des listes. L'approche simple mais naïve est le simple one-liner:

gen = (i for i in range(10))
list(gen)[3]

Mais rappelez-vous, les générateurs ne sont pas comme des listes. Ils ne stockent leurs résultats intermédiaires nulle part, vous ne pouvez donc pas revenir en arrière. Je vais démontrer le problème avec un exemple simple dans la réplique python:

>>> gen = (i for i in range(10))
>>> list(gen)[3]
3
>>> list(gen)[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

Une fois que vous avez commencé à utiliser un générateur pour obtenir la nième valeur de la séquence, celui-ci est dans un état différent et si vous tentez de récupérer la nième valeur, vous obtiendrez un résultat différent, ce qui risque de générer un bogue dans votre code. code.

Jetons un coup d'œil à un autre exemple, basé sur le code de la question.

On s’attendait initialement à ce que les éléments suivants impriment 4 deux fois.

gen = (i for i in range(10))
index = 4
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)

mais tapez ceci dans le repl et vous obtenez:

>>> gen = (i for i in range(10))
>>> index = 4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
9

Bonne chance pour retrouver cette bogue.

MODIFIER:

Comme indiqué, si le générateur est infiniment long, vous ne pouvez même pas le convertir en liste. L'expression list(gen) ne finira jamais.

Il existe un moyen de placer une enveloppe de mise en cache évaluée paresseusement autour d'un générateur infini pour lui donner l'apparence d'une liste infiniment longue dans laquelle vous pouvez indexer à volonté, mais cela mérite sa propre question et sa propre réponse, ce qui aurait des conséquences majeures sur les performances.

1
Brad Richardson

La première chose qui me vint à l’esprit fut:

gen = (i for i in xrange(10))
index = 5

for i, v in Zip(range(index), gen): pass

return v
0
Alexey