Quand devez-vous utiliser des expressions de générateur vs compréhensions de liste en Python et vice versa ?
Réponses
Trop de publicités?Jean de réponse est bon (ce qui interprétations de la liste sont mieux lorsque vous souhaitez effectuer une itération sur quelque chose plusieurs fois). Toutefois, il convient également de noter que vous devez utiliser une liste si vous voulez utiliser une partie quelconque de la liste des méthodes. Par exemple, le code suivant ne fonctionne pas:
def gen():
return (something for something in get_some_stuff())
print gen()[:2] # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists
Fondamentalement, il faut utiliser un générateur d'expression si tout ce que vous faites, c'est d'une itération à la fois. Si vous souhaitez stocker et utiliser les résultats générés, alors vous êtes probablement mieux avec une compréhension de liste.
Puisque les performances est la raison la plus courante pour choisir l'un sur l'autre, mon conseil est de ne pas s'inquiéter à ce sujet et il suffit de choisir un, si vous trouvez que votre programme s'exécute trop lentement, alors et seulement alors, vous revenez en arrière et de se soucier du réglage de votre code.
Itération sur l’expression de la génératrice ou la compréhension de liste fera la même chose. Toutefois, la maquette de la liste va créer la liste entière en mémoire tout d’abord alors que l’expression générateur va créer les points à la volée, alors vous êtes capable de l’utiliser pour des séquences très grands (et aussi infinies !).
Le point important est que la compréhension de liste crée une nouvelle liste. Le générateur crée un objet iterable objet que de "filtrer" les données source à la volée comme vous consommez de la bits.
Imaginez que vous avez un disque de 2 to fichier journal appelé "hugefile.txt" et vous voulez que le contenu et la durée pour toutes les lignes qui commencent par le mot "ENTRÉE".
Si vous essayez de commencer par l'écriture d'une compréhension de liste:
logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]
Cette slurps l'ensemble du fichier, chaque ligne, et les magasins de la correspondance des lignes de votre tableau. Ce tableau pourrait donc contenir jusqu'à 2 to de contenu. C'est beaucoup de RAM, et probablement pas pratique pour vos besoins.
Donc, au lieu de cela, nous pouvons utiliser un générateur pour appliquer un "filtre" à notre contenu. Aucune donnée n'est en fait de lire jusqu'à ce que nous commencer à parcourir le résultat.
logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))
Pas même une seule ligne a été lu à partir de notre fichier encore. En fait, disons que nous voulons filtre de notre résultat encore plus loin:
long_entries = ((line,length) for (line,length) in entry_lines if length > 80)
Rien n'a encore été lu, mais nous avons spécifié maintenant deux générateurs qui va agir sur nos données, comme nous le souhaitons.
Permet d'écrire notre les lignes filtrées à un autre fichier:
outfile = open("filtered.txt","a")
for entry,length in long_entries:
outfile.write(entry)
Maintenant nous lire le fichier d'entrée. Comme notre - for
boucle continue à demander d'autres lignes, l' long_entries
générateur de demandes lignes de l' entry_lines
générateur, de retour qu'à ceux dont la longueur est de plus de 80 caractères. Et à son tour, l' entry_lines
générateur de demandes de lignes (filtré comme indiqué) de l' logfile
itérateur, qui lit le fichier.
Ainsi, au lieu de "pousser" les données à votre sortie de la fonction sous la forme d'un entièrement rempli de liste, vous donnez la fonction de sortie d'une sorte de "tirer" les données uniquement lorsque c'est nécessaire. C'est dans notre cas beaucoup plus efficace, mais pas tout à fait flexible. Les générateurs sont un moyen, un seul passage; les données à partir du fichier journal que nous avons lu est immédiatement rejeté, donc on ne peut pas revenir sur une ancienne ligne. D'autre part, nous n'avons pas à vous soucier de garder les données à travers une fois que nous en avons terminé avec elle.
L'avantage d'un générateur d'expression, c'est qu'il utilise moins de mémoire puisqu'il n'a pas de construire l'ensemble de la liste à la fois. Générateur d'expressions sont utilisées au mieux lorsque la liste est un intermédiaire, comme en additionnant les résultats, ou la création d'un dict des résultats.
Par exemple:
sum(x*2 for x in xrange(256))
dict( ((k, some_func(k) for k in some_list_of_keys) )
L'avantage, c'est que la liste n'est pas entièrement formé, et donc de peu de mémoire (et qui devrait également être plus rapide)
Vous devriez, cependant, utiliser interprétations de la liste lorsque le produit final est une liste de. Vous n'allez pas enregistrer tout de mémoire à l'aide du générateur d'expressions, puisque vous voulez la liste générée. Vous obtenez également l'avantage d'être en mesure d'utiliser tout élément de la liste, comme les fonctions de tri ou inversé.
Par exemple:
reversed( [x*2 for x in xrange(256)] )