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.
une méthode consisterait à utiliser itertools.islice
>>> next(itertools.islice(xrange(10), 5, 5 + 1))
5
Vous pouvez le faire en utilisant count
comme exemple de générateur:
from itertools import islice, count
next(islice(count(), n, n+1))
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.
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.
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.
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