J'ai un générateur generator
et aussi une méthode pratique - 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?
Devrait generate_all
return
ou yield
? Je veux que les utilisateurs des deux méthodes l'utilisent de la même manière, c'est-à-dire
for x in generate_all()
devrait être égal à
some_list = get_the_list()
for x in generate(some_list)
Réponses:
Les générateurs effectuent une évaluation paresseuse,
return
ouyield
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 votregenerator
ne saurez riengenerate_all
, c'est parce que quand ilgenerator
est vraiment exécuté, vous avez déjà quitté lagenerate_all
fonction. Avecyield
dedans ça auragenerate_all
dans le traceback.Et s'il utilise
yield from
: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 queyield from ...
(oufor 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: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: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.
la source
Vous recherchez probablement une délégation de générateur (PEP380)
C'est assez concis et a également un certain nombre d'autres avantages, comme la possibilité de chaîner des itérables arbitraires / différents!
la source
list
? C'est un mauvais exemple, pas du vrai code collé dans la question, je devrais probablement le modifier.yield from map(do_something, iterable)
ou mêmeyield from (do_something(x) for x in iterable)
yield from
est donc inutile, sauf si votre wrapper fait autre chose générateur-y.return generator(list)
fait ce que vous voulez. Mais notez queserait équivalent, mais avec la possibilité de produire plus de valeurs après avoir
generator
épuisé. Par exemple:la source
yield from
etreturn
lorsque le consommateur du générateur athrows
une exception à l'intérieur de celui-ci - et avec d'autres opérations qui sont influencées par la trace de la pile.Les deux instructions suivantes semblent être fonctionnellement équivalentes dans ce cas particulier:
et
Ce dernier est à peu près le même que
L'
return
instruction renvoie le générateur que vous recherchez. Une instructionyield from
oryield
transforme l'ensemble de votre fonction en quelque chose qui renvoie un générateur, qui passe par celui que vous recherchez.D'un point de vue utilisateur, il n'y a pas de différence. En interne, cependant, le
return
est sans doute plus efficace car il ne s'enroule pasgenerator(list)
dans un générateur pass-thru superflu. Si vous prévoyez de faire un traitement sur les éléments du générateur encapsulé, utilisezyield
bien sûr une certaine forme .la source
Vous le feriez
return
.yield
ing * entraînerait l'generate_all()
évaluation d'un générateur lui-même, et invoquernext
ce générateur externe retournerait le générateur interne renvoyé par la première fonction, ce qui n'est pas ce que vous voudriez.*
Non comprisyield from
la source