Os geradores estão avaliando preguiçosamente returnou yieldse comportarão de maneira diferente quando você estiver depurando seu código ou se uma exceção for lançada.
Com returnqualquer exceção que você generatornão saiba generate_all, é porque quando generatoré realmente executado você já deixou a generate_allfunção. Com yieldlá vai ter generate_allno traceback.
def generator(some_list):
for i in some_list:
raise Exception('exception happened :-)')
yield i
def generate_all():
some_list = [1,2,3]
return generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-3-b19085eab3e1> in <module>
8 return generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-3-b19085eab3e1> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
E se estiver usando yield from:
def generate_all():
some_list = [1,2,3]
yield from generator(some_list)
for item in generate_all():
...
Exception Traceback (most recent call last)
<ipython-input-4-be322887df35> in <module>
8 yield from generator(some_list)
9
---> 10 for item in generate_all():
11 ...
<ipython-input-4-be322887df35> in generate_all()
6 def generate_all():
7 some_list = [1,2,3]
----> 8 yield from generator(some_list)
9
10 for item in generate_all():
<ipython-input-4-be322887df35> in generator(some_list)
1 def generator(some_list):
2 for i in some_list:
----> 3 raise Exception('exception happened :-)')
4 yield i
5
Exception: exception happened :-)
No entanto, isso tem um custo de desempenho. A camada adicional do gerador possui alguma sobrecarga. Então return, geralmente será um pouco mais rápido que yield from ...(ou for item in ...: yield item). Na maioria dos casos, isso não importa muito, porque o que você faz no gerador normalmente domina o tempo de execução, para que a camada adicional não seja perceptível.
No entanto, yieldpossui algumas vantagens adicionais: você não está restrito a uma única iterável, mas também pode gerar itens adicionais com facilidade:
def generator(some_list):
for i in some_list:
yield i
def generate_all():
some_list = [1,2,3]
yield 'start'
yield from generator(some_list)
yield 'end'
for item in generate_all():
print(item)
start
1
2
3
end
No seu caso, as operações são bastante simples e não sei se é necessário criar várias funções para isso, seria possível usar facilmente a mapexpressão incorporada ou de gerador:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
Ambos devem ser idênticos (exceto por algumas diferenças quando ocorrerem exceções) a serem usados. E se eles precisarem de um nome mais descritivo, você ainda poderá agrupá-los em uma função.
Existem vários auxiliares que envolvem operações muito comuns nos iterables internos e outros podem ser encontrados no itertoolsmódulo interno. Em casos tão simples, eu simplesmente recorreria a esses e somente para casos não triviais escreva seus próprios geradores.
Mas suponho que seu código real seja mais complicado, pelo que pode não ser aplicável, mas achei que não seria uma resposta completa sem mencionar alternativas.