Les générateurs effectuent une évaluation paresseuse, return
ou yield
se comportent différemment lorsque vous déboguez votre code ou si une exception est levée.
Avec return
toute exception qui se produit dans votre generator
ne saurez rien generate_all
, c'est parce que quand il generator
est vraiment exécuté, vous avez déjà quitté la generate_all
fonction. Avec yield
dedans ça aura generate_all
dans le 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 :-)
Et s'il utilise 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 :-)
Cependant, cela se fait au détriment des performances. La couche de générateur supplémentaire a une surcharge. Ce return
sera donc généralement un peu plus rapide que yield from ...
(ou for item in ...: yield item
). Dans la plupart des cas, cela n'a pas beaucoup d'importance, car tout ce que vous faites dans le générateur domine généralement le temps d'exécution de sorte que la couche supplémentaire ne soit pas perceptible.
Cela yield
présente cependant quelques avantages supplémentaires: vous n'êtes pas limité à un seul itérable, vous pouvez également facilement générer des éléments supplémentaires:
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
Dans votre cas, les opérations sont assez simples et je ne sais pas s'il est même nécessaire de créer plusieurs fonctions pour cela, on pourrait facilement utiliser simplement l' map
expression intégrée ou un générateur à la place:
map(do_something, get_the_list()) # map
(do_something(i) for i in get_the_list()) # generator expression
Les deux doivent être identiques (à l'exception de certaines différences lorsque des exceptions se produisent) à utiliser. Et s'ils ont besoin d'un nom plus descriptif, vous pouvez toujours les envelopper dans une seule fonction.
Il existe plusieurs assistants qui encapsulent des opérations très courantes sur les itérables intégrés et d'autres peuvent être trouvés dans le itertools
module intégré. Dans de tels cas simples, je recourrais simplement à ces derniers et seulement pour les cas non triviaux, écrivez vos propres générateurs.
Mais je suppose que votre vrai code est plus compliqué, ce qui peut ne pas être applicable, mais je pensais que ce ne serait pas une réponse complète sans mentionner des alternatives.