Quand devez-vous utiliser des expressions de générateur et quand devez-vous utiliser des compréhensions de liste en Python?
# Generator expression
(x*2 for x in range(256))
# List comprehension
[x*2 for x in range(256)]
python
list-comprehension
generator
Lecture seulement
la source
la source
[exp for x in iter]
être juste du sucrelist((exp for x in iter))
? ou y a-t-il une différence d'exécution?X = [x**2 for x in range(5)]; print x
avecY = list(y**2 for y in range(5)); print y
, le second donnera une erreur. En Python3, une compréhension de liste est en effet le sucre syntaxique d'une expression de générateur alimentéelist()
comme vous vous y attendiez, de sorte que la variable de boucle ne s'échappera plus .Réponses:
La réponse de John est bonne (les listes sont mieux comprises lorsque vous souhaitez répéter plusieurs fois quelque chose). Cependant, il convient également de noter que vous devez utiliser une liste si vous souhaitez utiliser l'une des méthodes de liste. Par exemple, le code suivant ne fonctionnera pas:
Fondamentalement, utilisez une expression de générateur si tout ce que vous faites est d'itérer une fois. Si vous souhaitez stocker et utiliser les résultats générés, alors vous êtes probablement mieux avec une liste de compréhension.
Étant donné que la performance est la raison la plus courante de choisir l'un plutôt que l'autre, mon conseil est de ne pas s'en inquiéter et d'en choisir un; si vous trouvez que votre programme s'exécute trop lentement, alors et seulement à ce moment-là, devriez-vous revenir en arrière et vous soucier de régler votre code.
la source
a = [1, 2, 3] b = [4, 5, 6] a.extend(b)
- un sera désormais [1, 2, 3, 4, 5, 6]. (Pouvez-vous ajouter des retours à la ligne dans les commentaires ??)a = (x for x in range(0,10)), b = [1,2,3]
par exemple.a.extend(b)
lève une exception.b.extend(a)
évaluera tout un, auquel cas il est inutile d'en faire un générateur en premier lieu.Itérer sur l' expression du générateur ou la compréhension de la liste fera la même chose. Cependant, la compréhension de la liste créera d'abord la liste entière en mémoire tandis que l' expression du générateur créera les éléments à la volée, vous pouvez donc l'utiliser pour des séquences très grandes (et aussi infinies!).
la source
itertools.count(n)
est une séquence infinie d'entiers, à partir de n,(2 ** item for item in itertools.count(n))
serait donc une séquence infinie des pouvoirs de2
départ à2 ** n
.Utilisez des compréhensions de liste lorsque le résultat doit être répété plusieurs fois ou lorsque la vitesse est primordiale. Utilisez des expressions de générateur où la plage est grande ou infinie.
Voir Expressions de générateur et compréhensions de liste pour plus d'informations.
la source
lists
sont-elles donc plus rapides que lesgenerator
expressions? En lisant la réponse de dF, il est apparu que c'était l'inverse.L'important est que la compréhension de la liste crée une nouvelle liste. Le générateur crée un objet itérable qui "filtre" le matériel source à la volée lorsque vous consommez les bits.
Imaginez que vous ayez un fichier journal de 2 To appelé "énormefichier.txt" et que vous souhaitiez le contenu et la longueur de toutes les lignes commençant par le mot "ENTRY".
Vous essayez donc de commencer par écrire une liste de compréhension:
Cela accélère le fichier entier, traite chaque ligne et stocke les lignes correspondantes dans votre tableau. Ce tableau peut donc contenir jusqu'à 2 To de contenu. C'est beaucoup de RAM, et ce n'est probablement pas pratique pour vos besoins.
Nous pouvons donc utiliser un générateur pour appliquer un "filtre" à notre contenu. Aucune donnée n'est réellement lue jusqu'à ce que nous commencions à répéter le résultat.
Pas même une seule ligne n'a encore été lue dans notre fichier. En fait, disons que nous voulons filtrer notre résultat encore plus loin:
Rien n'a encore été lu, mais nous avons spécifié maintenant deux générateurs qui agiront sur nos données comme nous le souhaitons.
Permet d'écrire nos lignes filtrées dans un autre fichier:
Maintenant, nous lisons le fichier d'entrée. Comme notre
for
boucle continue de demander des lignes supplémentaires, lelong_entries
générateur demande des lignes auentry_lines
générateur, renvoyant uniquement celles dont la longueur est supérieure à 80 caractères. Et à son tour, leentry_lines
générateur demande des lignes (filtrées comme indiqué) à l'logfile
itérateur, qui à son tour lit le fichier.Ainsi, au lieu de "pousser" les données vers votre fonction de sortie sous la forme d'une liste entièrement remplie, vous donnez à la fonction de sortie un moyen de "tirer" les données uniquement lorsque cela est nécessaire. C'est dans notre cas beaucoup plus efficace, mais pas aussi flexible. Les générateurs sont à sens unique, un passage; les données du fichier journal que nous avons lu sont immédiatement supprimées, nous ne pouvons donc pas revenir à une ligne précédente. D'un autre côté, nous n'avons pas à nous soucier de conserver les données une fois que nous en avons terminé.
la source
L'avantage d'une expression de générateur est qu'elle utilise moins de mémoire car elle ne construit pas la liste entière à la fois. Les expressions de générateur sont mieux utilisées lorsque la liste est un intermédiaire, comme la somme des résultats ou la création d'un dict à partir des résultats.
Par exemple:
L'avantage est que la liste n'est pas complètement générée, et donc peu de mémoire est utilisée (et devrait également être plus rapide)
Vous devez cependant utiliser des compréhensions de liste lorsque le produit final souhaité est une liste. Vous n'allez pas enregistrer de mémoire à l'aide d'expressions de générateur, car vous voulez la liste générée. Vous avez également l'avantage de pouvoir utiliser n'importe quelle fonction de liste comme triée ou inversée.
Par exemple:
la source
sum(x*2 for x in xrange(256))
sorted
etreversed
fonctionne très bien sur toutes les expressions de générateur itérables incluses.Lors de la création d'un générateur à partir d'un objet mutable (comme une liste), sachez que le générateur sera évalué sur l'état de la liste au moment de l'utilisation du générateur, et non au moment de la création du générateur:
S'il y a une chance que votre liste soit modifiée (ou un objet modifiable à l'intérieur de cette liste) mais vous avez besoin de l'état à la création du générateur, vous devez utiliser une compréhension de liste à la place.
la source
Parfois, vous pouvez vous en tirer avec la fonction tee d' itertools , elle renvoie plusieurs itérateurs pour le même générateur qui peuvent être utilisés indépendamment.
la source
J'utilise le module Hadoop Mincemeat . Je pense que c'est un excellent exemple pour prendre note de:
Ici, le générateur extrait des nombres d'un fichier texte (aussi gros que 15 Go) et applique des calculs simples à ces nombres à l'aide de la réduction de carte de Hadoop. Si je n'avais pas utilisé la fonction de rendement, mais plutôt une compréhension de liste, il aurait fallu beaucoup plus de temps pour calculer les sommes et la moyenne (sans parler de la complexité de l'espace).
Hadoop est un excellent exemple pour utiliser tous les avantages des générateurs.
la source