Tengo un generador generator
y también un método conveniente para ello generate_all
.
def generator(some_list):
for i in some_list:
yield do_something(i)
def generate_all():
some_list = get_the_list()
return generator(some_list) # <-- Is this supposed to be return or yield?
Debería generate_all
return
o yield
? Quiero que los usuarios de ambos métodos lo usen igual, es decir
for x in generate_all()
debe ser igual a
some_list = get_the_list()
for x in generate(some_list)
Respuestas:
Los generadores tienen una evaluación perezosa,
return
oyield
se comportarán de manera diferente cuando depure su código o si se produce una excepción.Con
return
cualquier excepción que ocurra en tugenerator
no sabrás nadagenerate_all
, eso es porque cuandogenerator
realmente se ejecuta ya has dejado lagenerate_all
función. Conyield
allí tendrágenerate_all
en el rastreo.Y si está usando
yield from
:Sin embargo, esto tiene un costo de rendimiento. La capa del generador adicional tiene algo de sobrecarga. Por
return
lo tanto , generalmente será un poco más rápido queyield from ...
(ofor item in ...: yield item
). En la mayoría de los casos, esto no importará mucho, porque todo lo que hagas en el generador generalmente domina el tiempo de ejecución para que la capa adicional no se note.Sin embargo,
yield
tiene algunas ventajas adicionales: no está restringido a un solo iterable, también puede generar fácilmente elementos adicionales:En su caso, las operaciones son bastante simples y no sé si incluso es necesario crear múltiples funciones para esto, uno podría usar fácilmente la
map
expresión incorporada o una expresión generadora:Ambos deben ser idénticos (excepto algunas diferencias cuando ocurren excepciones) para usar. Y si necesitan un nombre más descriptivo, entonces aún podría envolverlos en una función.
Hay múltiples ayudantes que envuelven operaciones muy comunes en iterables integrados y se pueden encontrar más en el
itertools
módulo incorporado . En casos tan simples, simplemente recurriría a estos y solo para casos no triviales escriba sus propios generadores.Pero supongo que su código real es más complicado, por lo que puede no ser aplicable, pero pensé que no sería una respuesta completa sin mencionar alternativas.
fuente
Probablemente esté buscando Delegación de generador (PEP380)
Es bastante conciso y también tiene una serie de otras ventajas, como ser capaz de encadenar iterables arbitrarios / diferentes.
fuente
list
? Es un mal ejemplo, no es un código real pegado en la pregunta, probablemente debería editarlo.yield from map(do_something, iterable)
o inclusoyield from (do_something(x) for x in iterable)
yield from
lo tanto, no tiene sentido a menos que su contenedor haga algo más generador-y.return generator(list)
hace lo que quieres Pero ten en cuenta quesería equivalente, pero con la oportunidad de producir más valores después de que
generator
se agote. Por ejemplo:fuente
yield from
yreturn
cuando el consumidor del generador esthrows
una excepción dentro de él, y con otras operaciones que están influenciadas por el seguimiento de la pila.Las siguientes dos declaraciones parecerán ser funcionalmente equivalentes en este caso particular:
y
El último es aproximadamente lo mismo que
La
return
declaración devuelve el generador que está buscando. Una declaraciónyield from
oyield
convierte toda su función en algo que devuelve un generador, que pasa por el que está buscando.Desde el punto de vista del usuario, no hay diferencia. Sin embargo, internamente
return
se puede decir que es más eficiente ya que no se envuelvegenerator(list)
en un generador de paso superfluo. Si planea realizar algún procesamiento en los elementos del generador envuelto, utilice alguna forma,yield
por supuesto.fuente
Lo haría con
return
él.yield
ing * provocaríagenerate_all()
evaluar a un generador en sí mismo, y llamarnext
a ese generador externo devolvería el generador interno devuelto por la primera función, que no es lo que desearía.*
No incluídoyield from
fuente