Alejandro
Tengo una función de generador como la siguiente:
def myfunct():
...
yield result
La forma habitual de llamar a esta función sería:
for r in myfunct():
dostuff(r)
Mi pregunta, ¿hay alguna manera de obtener solo un elemento del generador cuando lo desee? Por ejemplo, me gustaría hacer algo como:
while True:
...
if something:
my_element = pick_just_one_element(myfunct())
dostuff(my_element)
...
Sven Marnach
Crea un generador usando
g = myfunct()
Cada vez que desee un artículo, utilice
next(g)
(o g.next()
en Python 2.5 o inferior).
Si el generador sale, subirá StopIteration
. Puede capturar esta excepción si es necesario, o usar el default
argumento a next()
:
next(g, default_value)
-
Tenga en cuenta que solo generará StopIteration cuando intente usar g.next() después de que se haya proporcionado el último elemento en g.
– pato salvaje
19 de enero de 2011 a las 22:11
-
next(gen, default)
también se puede utilizar para evitar laStopIteration
excepción. Por ejemplonext(g, None)
para un generador de cadenas producirá una cadena o Ninguno después de que finalice la iteración.– Atila
6 de marzo de 2013 a las 14:18
-
en Python 3000, next() es __next__()
–Jonathan Baldwin
10 de abril de 2014 a las 2:52
-
@JonathanBaldwin: Su comentario es algo engañoso. En Python 3, usaría la segunda sintaxis dada en mi respuesta,
next(g)
. Esto llamará internamenteg.__next__()
pero en realidad no tienes que preocuparte por eso, al igual que normalmente no te importa esolen(a)
llamadas internasa.__len__()
.– Sven Marnach
10 de abril de 2014 a las 10:31
-
Debería haber sido más claro.
g.next()
esg.__next__()
en py3k. el incorporadonext(iterator)
ha existido desde Python 2.6, y es lo que debe usarse en todo el nuevo código de Python, y es trivial volver a implementarlo si necesita admitir py–Jonathan Baldwin
10/04/2014 a las 19:58
kxr
Para elegir solo un elemento de un uso generador break
en un for
declaración, o list(itertools.islice(gen, 1))
De acuerdo con su ejemplo (literalmente) puede hacer algo como:
while True:
...
if something:
for my_element in myfunct():
dostuff(my_element)
break
else:
do_generator_empty()
Si tu quieres “obtener sólo un elemento de la [once generated] generador cuando yo quiera(Supongo que el 50% es la intención original, y la intención más común) entonces:
gen = myfunct()
while True:
...
if something:
for my_element in gen:
dostuff(my_element)
break
else:
do_generator_empty()
De esta manera el uso explícito de generator.next()
se puede evitar, y el manejo de fin de entrada no requiere (críptico) StopIteration
manejo de excepciones o comparaciones de valores predeterminados adicionales.
los else:
de for
La sección de declaración solo es necesaria si desea hacer algo especial en caso de final del generador.
Nota sobre next()
/ .next()
:
En Python3 el .next()
el método fue renombrado a .__next__()
por una buena razón: se considera de bajo nivel (PEP 3114). Antes de Python 2.6, la función incorporada next()
no existió. Y hasta se discutió mudarse next()
hacia operator
módulo (que habría sido inteligente), debido a su rara necesidad y cuestionable inflación de nombres incorporados.
Usando next()
sin defecto sigue siendo una práctica de muy bajo nivel: lanzar el críptico StopIteration
como un rayo de la nada en el código de aplicación normal abiertamente. y usando next()
con centinela predeterminado, que debería ser la única opción para un next()
directamente en builtins
– es limitado y, a menudo, da motivos para una extraña lógica/legibilidad no pitónica.
En pocas palabras: usar next() debería ser muy raro, como usar funciones de operator
módulo. Usando for x in iterator
, islice
, list(iterator)
y otras funciones que aceptan un iterador sin problemas es la forma natural de usar iteradores en el nivel de aplicación, y siempre es posible. next()
es de bajo nivel, un concepto adicional, no obvio, como muestra la pregunta de este hilo. Mientras que, por ejemplo, usando break
en for
es convencional.
-
Esto es demasiado trabajo para solo obtener el primer elemento del resultado de una lista. A menudo, no necesito que sea perezoso, pero no tengo otra opción en py3. ¿No hay algo parecido a
mySeq.head
?– Proyectos de la costa oeste
3 de marzo de 2018 a las 15:57
andrii
Generator es una función que produce un iterador. Por lo tanto, una vez que tenga una instancia de iterador, use Siguiente() para obtener el siguiente elemento del iterador. Como ejemplo, use la función next() para obtener el primer elemento y luego use for in
para procesar los elementos restantes:
# create new instance of iterator by calling a generator function
items = generator_function()
# fetch and print first item
first = next(items)
print('first item:', first)
# process remaining items:
for item in items:
print('next item:', item)
marca mcdonald
Puede elegir elementos específicos mediante la desestructuración, por ejemplo:
>>> first, *middle, last = range(10)
>>> first
0
>>> middle
[1, 2, 3, 4, 5, 6, 7, 8]
>>> last
9
Tenga en cuenta que esto consumirá su generador, por lo que, si bien es muy legible, es menos eficiente que algo como next()
y ruinoso en infinitos generadores:
>>> first, *rest = itertools.count()
🔥🔥🔥
No creo que haya una forma conveniente de recuperar un valor arbitrario de un generador. El generador proporcionará un método next() para recorrerse a sí mismo, pero la secuencia completa no se produce inmediatamente para ahorrar memoria. Esa es la diferencia funcional entre un generador y una lista.
John
generator = myfunct()
while True:
my_element = generator.next()
asegúrese de capturar la excepción lanzada después de tomar el último elemento
dave rove
Para aquellos de ustedes que buscan en estas respuestas un ejemplo de trabajo completo para Python3… bueno, aquí tienen:
def numgen():
x = 1000
while True:
x += 1
yield x
nums = numgen() # because it must be the _same_ generator
for n in range(3):
numnext = next(nums)
print(numnext)
Esto da como resultado:
1001
1002
1003