I generatori lo stanno valutando in modo pigro returno yieldsi comporteranno in modo diverso durante il debug del codice o se viene generata un'eccezione.
Con returnqualsiasi eccezione che accade nel tuo generatornon ne saprai nulla generate_all, perché perché quando generatorviene realmente eseguito hai già lasciato la generate_allfunzione. Con yielddentro avrà generate_allnel 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 sta 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 :-)
Tuttavia, questo viene a scapito delle prestazioni. Il livello generatore aggiuntivo ha un certo sovraccarico. Quindi returnsarà generalmente un po 'più veloce di yield from ...(o for item in ...: yield item). Nella maggior parte dei casi questo non importa molto, perché qualsiasi cosa tu faccia nel generatore domina in genere il tempo di esecuzione in modo che il livello aggiuntivo non sia evidente.
Tuttavia yieldpresenta alcuni vantaggi aggiuntivi: non sei limitato a un singolo iterabile, puoi anche facilmente produrre elementi aggiuntivi:
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
Nel tuo caso le operazioni sono abbastanza semplici e non so se sia necessario creare funzioni multiple per questo, si potrebbe semplicemente usare l' mapespressione incorporata o un generatore invece:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
Entrambi dovrebbero essere identici (ad eccezione di alcune differenze quando si verificano eccezioni) da utilizzare. E se hanno bisogno di un nome più descrittivo, allora potresti ancora avvolgerli in una funzione.
Esistono diversi helper che racchiudono operazioni molto comuni sugli iterabili incorporati e altri sono disponibili nel itertoolsmodulo integrato. In casi così semplici ricorrerei semplicemente a questi e solo per casi non banali scrivere i propri generatori.
Ma suppongo che il tuo vero codice sia più complicato, quindi potrebbe non essere applicabile, ma ho pensato che non sarebbe stata una risposta completa senza menzionare le alternative.