Quand utiliser une compréhension de liste en Python – Real Python

By | novembre 6, 2019

trouver un expert Python

Python est célèbre pour vous permettre d’écrire un code élégant, facile à écrire et presque aussi facile à lire que l’anglais ordinaire. Une des caractéristiques les plus distinctives de la langue est la compréhension de la liste, que vous pouvez utiliser pour créer des fonctionnalités puissantes dans une seule ligne de code. Cependant, de nombreux développeurs ont du mal à exploiter pleinement les fonctionnalités plus avancées de la compréhension de liste en Python. Certains programmeurs les utilisent même trop, ce qui peut conduire à un code moins efficace et plus difficile à lire.

À la fin de ce didacticiel, vous comprendrez toute la puissance des listes de compréhension Python et vous expliquerez comment les utiliser facilement. Vous comprendrez également les compromis qui s’imposent pour les utiliser, ce qui vous permet de déterminer quand d’autres approches sont préférables.

Dans ce tutoriel, vous allez apprendre à:

  • Réécrire les boucles et carte() appelle en tant que compréhension de la liste en python
  • Choisir entre les compréhensions, les boucles et carte() appels
  • Augmentez vos compréhensions avec logique conditionnelle
  • Utiliser les compréhensions remplacer filtre()
  • Profil votre code pour résoudre les questions de performance

Comment créer des listes en Python

Il existe différentes manières de créer des listes en Python. Pour mieux comprendre les inconvénients de l’utilisation d’une compréhension de liste en Python, voyons d’abord comment créer des listes avec ces approches.

En utilisant pour Boucles

Le type de boucle le plus courant est le pour boucle. Vous pouvez utiliser un pour boucle pour créer une liste d'éléments en trois étapes:

  1. Instancier une liste vide.
  2. Boucle sur un itératif ou une gamme d'éléments.
  3. Ajoutez chaque élément à la fin de la liste.

Si vous souhaitez créer une liste contenant les dix premiers carrés parfaits, vous pouvez compléter ces étapes en trois lignes de code:

>>>

>>> des carrés = []
>>> pour je dans intervalle(dix):
...     des carrés.ajouter(je * je)
>>> des carrés
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Ici, vous instanciez une liste vide, des carrés. Ensuite, vous utilisez un pour boucle pour parcourir gamme (10). Enfin, vous multipliez chaque nombre par lui-même et ajoutez le résultat à la fin de la liste.

En utilisant carte() Objets

carte() fournit une approche alternative basée sur la programmation fonctionnelle. Vous passez dans une fonction et un iterable, et carte() va créer un objet. Cet objet contient le résultat obtenu en exécutant chaque élément itérable via la fonction fournie.

A titre d'exemple, considérons une situation dans laquelle vous devez calculer le prix après impôt pour une liste de transactions:

>>>

>>> txns = [[[[1,09, 23,56, 57,84, 4,56, 6,78]
>>> TAUX D'IMPOSITION = .08
>>> def get_price_with_tax(txn):
...     revenir txn * (1 + TAUX D'IMPOSITION)
>>> prix_finaux = carte(get_price_with_tax, txns)
>>> liste(prix_finaux)
[1.1772000000000002, 25.4448, 62.467200000000005, 4.9248, 7.322400000000001]

Ici, vous avez un itérable txns et une fonction get_price_with_tax (). Vous passez ces deux arguments à carte()et stocker l'objet résultant dans prix_finaux. Vous pouvez facilement convertir cet objet de carte en une liste en utilisant liste().

Utilisation des listes de compréhension

La compréhension de liste est une troisième façon de faire des listes. Avec cette approche élégante, vous pouvez réécrire le pour boucle du premier exemple dans une seule ligne de code:

>>>

>>> des carrés = [[[[je * je pour je dans intervalle(dix)]
>>> des carrés
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Plutôt que de créer une liste vide et d'ajouter chaque élément à la fin, vous définissez simplement la liste et son contenu en même temps en respectant le format suivant:

>>>

new_list = [expression for member in iterable]

Chaque compréhension de liste en Python comprend trois éléments:

  1. expression est le membre lui-même, un appel à une méthode ou toute autre expression valide qui renvoie une valeur. Dans l'exemple ci-dessus, l'expression i * i est le carré de la valeur du membre.
  2. membre est l'objet ou la valeur dans la liste ou itérable. Dans l'exemple ci-dessus, la valeur du membre est je.
  3. iterable est une liste, un ensemble, une séquence, un générateur ou tout autre objet pouvant renvoyer ses éléments un à un. Dans l'exemple ci-dessus, l'itérable est gamme (10).

Parce que le expression L’exigence est si flexible qu’une compréhension de la liste en Python fonctionne bien dans de nombreux endroits où vous utiliseriez carte(). Vous pouvez réécrire l'exemple de tarification avec sa propre compréhension de liste:

>>>

>>> txns = [[[[1,09, 23,56, 57,84, 4,56, 6,78]
>>> TAUX D'IMPOSITION = .08
>>> def get_price_with_tax(txn):
...     revenir txn * (1 + TAUX D'IMPOSITION)
>>> prix_finaux = [[[[get_price_with_tax(je) pour je dans txns]
>>> prix_finaux
[1.1772000000000002, 25.4448, 62.467200000000005, 4.9248, 7.322400000000001]

La seule distinction entre cette mise en œuvre et carte() est que la compréhension de la liste en Python renvoie une liste et non un objet de la carte.

Avantages de l'utilisation de la compréhension de liste

Les compréhensions de liste sont souvent décrites comme étant plus pythoniques que les boucles ou carte(). Mais au lieu d’accepter aveuglément cette évaluation, il vaut la peine de comprendre les avantages de l’utilisation d’une liste de compréhension en Python par rapport aux alternatives. Plus tard, vous découvrirez quelques scénarios dans lesquels les alternatives sont un meilleur choix.

L’utilisation d’une liste de compréhension en Python présente l’un des principaux avantages: c’est un outil unique que vous pouvez utiliser dans de nombreuses situations différentes. En plus de la création de liste standard, la compréhension de liste peut également être utilisée pour la cartographie et le filtrage. Vous n’avez pas à utiliser une approche différente pour chaque scénario.

C’est la raison principale pour laquelle les compréhensions de liste sont considérées Pythonique, en tant que Python, englobe des outils simples et puissants que vous pouvez utiliser dans une grande variété de situations. En outre, chaque fois que vous utilisez une liste de compréhension en Python, vous n’avez pas besoin de vous rappeler le bon ordre des arguments comme vous le feriez lorsque vous appelez. carte().

Les compréhensions de liste sont aussi plus déclaratif que les boucles, ce qui signifie qu’elles sont plus faciles à lire et à comprendre. Les boucles vous obligent à vous concentrer sur la création de la liste. Vous devez créer manuellement une liste vide, passer en boucle sur les éléments et les ajouter à la fin de la liste. Avec une liste de compréhension en Python, vous pouvez plutôt vous concentrer sur quoi vous voulez aller dans la liste et faites confiance à Python pour prendre soin de Comment la construction de la liste a lieu.

Comment surcharger vos compréhensions

Afin de comprendre toute la valeur que la compréhension de liste peut apporter, il est utile de comprendre leur gamme de fonctionnalités possibles. Vous voudrez également comprendre les modifications apportées à la compréhension de la liste dans Python 3.8.

Utilisation de la logique conditionnelle

Plus tôt, vous avez vu cette formule sur la façon de créer des compréhensions de liste:

>>>

new_list = [expression for member in iterable]

Bien que cette formule soit exacte, elle est aussi un peu incomplète. Une description plus complète de la formule de compréhension ajoute le support pour facultatif conditionnels. Le moyen le plus courant d’ajouter une logique conditionnelle à une compréhension de liste est d’ajouter un conditionnel à la fin de l’expression:

>>>

new_list = [expression for member in iterable (if conditional)]

Ici, votre déclaration conditionnelle vient juste avant la parenthèse fermante.

Les conditions sont importantes car elles permettent aux interprétations de liste de filtrer les valeurs non désirées, ce qui nécessiterait normalement un appel à filtre():

>>>

>>> phrase = "la fusée est revenue de mars"
>>> voyelles = [[[[je pour je dans phrase si je dans 'aeiou']
>>> voyelles
['e', 'o', 'e', 'a', 'e', 'a', 'o', 'a']

Dans ce bloc de code, l’instruction conditionnelle filtre tous les caractères de phrase qui ne sont pas une voyelle.

Le conditionnel peut tester toute expression valide. Si vous avez besoin d'un filtre plus complexe, vous pouvez même déplacer la logique conditionnelle vers une fonction distincte:

>>>

>>> phrase = 'La fusée, qui s'appelait Ted, est revenue 
... de Mars parce que ses amis lui manquaient.
>>> def is_consonant(lettre):
...     voyelles = 'aeiou'
...     revenir lettre.Isalpha() et lettre.inférieur() ne pas dans voyelles
>>> les consonnes = [[[[je pour je dans phrase si is_consonant(je)]
''

''['T''h''r''c''k''t''w''h''w''s''n''m''d'['T''h''r''c''k''t''w''h''w''s''n''m''d'
'T', 'd', 'c', 'm', 'b', 'c', 'k', 'f', 'r', 'm', 'm', 'r', 's' ',' b ', 
'c', 's', 'h', 'm', 's', 's', 'd', 'h', 's', 'f', 'r', 'n', 'd' ',' s ']

Ici, vous créez un filtre complexe is_consonant () et passez cette fonction en tant qu'instruction conditionnelle pour la compréhension de votre liste. Notez que la valeur du membre je est également passé comme argument à votre fonction.

Vous pouvez placer le conditionnel à la fin de l'instruction pour un filtrage simple, mais si vous voulez changement une valeur de membre au lieu de filtrer? Dans ce cas, il est utile de placer le conditionnel près de la début de l'expression:

>>>

new_list = [expression (if conditional) for member in iterable]

Avec cette formule, vous pouvez utiliser la logique conditionnelle pour sélectionner plusieurs options de sortie possibles. Par exemple, si vous avez une liste de prix, vous pouvez alors remplacer les prix négatifs par 0 et laissez les valeurs positives inchangées:

>>>

>>> prix_original = [[[[1,25, -9h45, 10.22, 3,78, -5,92, 1,16]
>>> des prix = [[[[je si je > 0 autre 0 pour je dans prix_original]
>>> des prix
[1.25, 0, 10.22, 3.78, 0, 1.16]

Ici, ton expression je contient une déclaration conditionnelle, si je> 0 sinon 0. Ceci dit à Python de sortir la valeur de je si le nombre est positif, mais changer je à 0 si le nombre est négatif. Si cela semble insurmontable, il peut être utile de considérer la logique conditionnelle comme une fonction propre:

>>>

>>> def get_price(prix):
...     revenir prix si prix > 0 autre 0
>>> des prix = [[[[get_price(je) pour je dans prix_original]
>>> des prix
[1.25, 0, 10.22, 3.78, 0, 1.16]

Maintenant, votre déclaration conditionnelle est contenue dans get_price (), et vous pouvez l’utiliser dans le cadre de l’expression de compréhension de liste.

Utilisation de la compréhension de jeux et de dictionnaires

Bien que la compréhension de liste en Python soit un outil commun, vous pouvez également créer des compréhensions de jeu et de dictionnaire. UNE ensemble de compréhension est presque exactement la même chose qu'une compréhension de liste en Python. La différence est que la compréhension des ensembles permet de s’assurer que la sortie ne contient pas de doublons. Vous pouvez créer une compréhension d'ensemble en utilisant des accolades au lieu de crochets:

>>>

>>> citation = "la vie trouve un chemin"
>>> unique_vowels = je pour je dans citation si je dans 'aeiou'
>>> unique_vowels
'a', 'e', ​​'u', 'i'

Votre compréhension d'ensemble génère toutes les voyelles uniques qu'il a trouvées dans citation. Contrairement aux listes, les ensembles ne garantissent pas que les éléments seront enregistrés dans un ordre particulier. C’est pourquoi le premier membre de l’ensemble est une, même si la première voyelle de citation est je.

Compréhensions du dictionnaire sont similaires, avec l'obligation supplémentaire de définir une clé:

>>>

>>> des carrés = je: je * je pour je dans intervalle(dix)
>>> des carrés
0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81

Pour créer le des carrés dictionnaire, vous utilisez des accolades () ainsi qu’une paire clé-valeur (i: i * i) dans votre expression.

Utiliser l'opérateur de morse

Python 3.8 introduira l'expression d'affectation, également connue sous le nom de opérateur de morse. Pour comprendre comment l’utiliser, considérons l’exemple suivant.

Supposons que vous deviez adresser dix requêtes à une API qui renverra des données de température. Vous souhaitez uniquement obtenir des résultats supérieurs à 100 degrés Fahrenheit. Supposons que chaque demande renvoie des données différentes. Dans ce cas, il n’est pas possible d’utiliser une compréhension de liste en Python pour résoudre le problème. La formule expression for member in iterable (si conditionnel) ne fournit aucun moyen pour que le conditionnel affecte des données à une variable à laquelle l'expression peut accéder.

le opérateur de morse résout ce problème. Il vous permet d'exécuter une expression tout en affectant simultanément la valeur de sortie à une variable. L’exemple suivant montre comment cela est possible en utilisant get_weather_data () pour générer de fausses données météorologiques:

>>>

>>> importation Aléatoire
>>> def get_weather_data():
...     revenir Aléatoire.randrange(90, 110)
>>> hot_temps = [[[[temp pour _ dans intervalle(20) si (temp := get_weather_data()) > = 100]
>>> hot_temperatures
[107, 102, 109, 104, 107, 109, 108, 101, 104]

Il n’est souvent pas nécessaire d’utiliser l’expression d’affectation dans une compréhension de liste en Python, mais c’est un outil utile à avoir à votre disposition lorsque cela est nécessaire.

Quand ne pas utiliser une compréhension de liste en Python

La compréhension des listes est utile et peut vous aider à écrire un code élégant, facile à lire et à déboguer, mais ce n’est pas le bon choix dans toutes les circonstances. Ils pourraient ralentir votre code ou utiliser plus de mémoire. Si votre code est moins performant ou plus difficile à comprendre, il est probablement préférable de choisir une alternative.

Attention aux compréhensions imbriquées

Les compréhensions peuvent être imbriqué créer des combinaisons de listes, dictionnaires et ensembles au sein d’une collection. Par exemple, supposons qu'un laboratoire climatique surveille la température élevée dans cinq villes différentes pendant la première semaine de juin. La structure de données idéale pour stocker ces données pourrait être une compréhension de liste Python imbriquée dans une compréhension de dictionnaire:

>>>

>>> villes = [[[["Austin", 'Tacoma', 'Topeka', 'Sacramento', 'Charlotte']
>>> temps = ville: [[[[0 pour _ dans intervalle(sept)] pour ville dans villes
>>> temps

                'Austin': [0, 0, 0, 0, 0, 0, 0],
                'Tacoma': [0, 0, 0, 0, 0, 0, 0],
                'Topeka': [0, 0, 0, 0, 0, 0, 0],
                'Sacramento': [0, 0, 0, 0, 0, 0, 0],
                'Charlotte': [0, 0, 0, 0, 0, 0, 0]

Vous créez la collection externe temps avec une compréhension du dictionnaire. L'expression est une paire clé-valeur, qui contient encore une autre compréhension. Ce code générera rapidement une liste de données pour chaque ville de villes.

Les listes imbriquées sont un moyen courant de créer matrices, qui sont souvent utilisés à des fins mathématiques. Regardez le bloc de code ci-dessous:

>>>

>>> matrice = [[[[[[[[je pour je dans intervalle(5)] pour _ dans intervalle(6)]
>>> matrice
[[[[
    [0, 1, 2, 3, 4],
    [0, 1, 2, 3, 4],
    [0, 1, 2, 3, 4],
    [0, 1, 2, 3, 4],
    [0, 1, 2, 3, 4],
    [0, 1, 2, 3, 4]
]

La compréhension de la liste externe [... for _ in range(6)] crée six lignes, tandis que la compréhension de la liste intérieure [i for i in range(5)] remplit chacune de ces lignes avec des valeurs.

Jusqu'ici, le but de chaque compréhension imbriquée est assez intuitif. Cependant, il existe d'autres situations, telles que aplanissement listes imbriquées, où la logique rend votre code plus confus. Prenons cet exemple, qui utilise une compréhension de liste imbriquée pour aplatir une matrice:

>>>

matrice =[[[[
...     [[[[0, 0, 0],
...     [[[[1, 1, 1],
...     [[[[2, 2, 2],
... ]
>>> plat = [[[[num pour rangée dans matrice pour num dans rangée]
>>> plat
[0, 0, 0, 1, 1, 1, 2, 2, 2]

Le code pour aplatir la matrice est concis, mais il n’est peut-être pas si intuitif de comprendre comment cela fonctionne. D'autre part, si vous deviez utiliser pour boucles pour aplatir la même matrice, votre code sera beaucoup plus simple:

>>>

>>> matrice = [[[[
...     [[[[0, 0, 0],
...     [[[[1, 1, 1],
...     [[[[2, 2, 2],
... ]
>>> plat = []
>>> pour rangée dans matrice:
...     pour num dans rangée:
...         plat.ajouter(num)
...
>>> plat
[0, 0, 0, 1, 1, 1, 2, 2, 2]

Vous pouvez maintenant voir que le code parcourt une ligne de la matrice à la fois, en extrayant tous les éléments de cette ligne avant de passer à la suivante.

Bien que la compréhension d’une liste imbriquée sur une seule ligne puisse sembler plus pythonique, le plus important est d’écrire du code que votre équipe peut facilement comprendre et modifier. Lorsque vous choisissez votre approche, vous devrez faire appel à votre jugement si vous pensez que la compréhension aide ou nuit à la lisibilité.

Choisir des générateurs pour des jeux de données volumineux

Une compréhension de liste en Python fonctionne en chargeant la liste de sortie entière en mémoire. Cela convient généralement aux listes de petite taille, voire de taille moyenne. Si vous voulez additionner les carrés des mille premiers nombres entiers, alors une compréhension de liste résoudra ce problème admirablement:

>>>

>>> somme([[[[je * je pour je dans intervalle(1000)])
332833500

Mais si vous vouliez résumer les carrés du premier milliard des nombres entiers? Si vous avez ensuite essayé sur votre machine, vous remarquerez peut-être que votre ordinateur ne répond plus. C’est parce que Python tente de créer une liste contenant un milliard d’entiers, ce qui consomme plus de mémoire que ne le souhaiterait votre ordinateur. Votre ordinateur peut ne pas avoir les ressources nécessaires pour générer une liste énorme et la stocker en mémoire. Si vous tentez malgré tout de le faire, votre machine pourrait alors ralentir ou même se bloquer.

Lorsque la taille d’une liste devient problématique, il est souvent utile d’utiliser un générateur au lieu d’une compréhension de liste en Python. UNE Générateur ne crée pas une seule structure de données volumineuse en mémoire, mais renvoie une valeur itérable. Votre code peut demander la valeur suivante à l'itérable autant de fois que nécessaire ou jusqu'à ce que vous ayez atteint la fin de votre séquence, tout en ne stockant qu'une seule valeur à la fois.

Si vous additionnez le premier milliard de carrés avec un générateur, votre programme fonctionnera probablement pendant un certain temps, mais votre ordinateur ne devrait pas geler. L'exemple ci-dessous utilise un générateur:

>>>

>>> somme(je * je pour je dans intervalle(1000000000))
333333332833333333500000000

Vous pouvez dire qu’il s’agit d’un générateur car l’expression n’est pas entourée de crochets ni d’accolades. Les générateurs peuvent éventuellement être entourés de parenthèses.

L’exemple ci-dessus nécessite toujours beaucoup de travail, mais il effectue les opérations paresseusement. En raison d'une évaluation paresseuse, les valeurs ne sont calculées que lorsqu'elles sont explicitement demandées. Après que le générateur donne une valeur (par exemple, 567 * 567), il peut ajouter cette valeur à la somme en cours, puis ignorer cette valeur et générer la valeur suivante (568 * 568). Lorsque la fonction sum demande la valeur suivante, le cycle recommence. Ce processus réduit l'encombrement de la mémoire.

carte() fonctionne également paresseusement, ce qui signifie que la mémoire ne sera pas un problème si vous choisissez de l’utiliser dans ce cas:

>>>

>>> somme(carte(lambda je: je*je, intervalle(1000000000)))
333333332833333333500000000

C’est à vous de choisir si vous préférez l’expression du générateur ou carte().

Profil pour optimiser les performances

Alors, quelle approche est la plus rapide? Devez-vous utiliser les listes de compréhension ou une de leurs alternatives? Plutôt que d’adhérer à une seule règle qui soit vraie dans tous les cas, il est plus utile de vous demander si les performances questions dans vos circonstances spécifiques. Sinon, il est généralement préférable de choisir l’approche qui conduit au code le plus propre!

Si vous êtes dans un scénario où les performances sont importantes, il est généralement préférable de profil différentes approches et écouter les données. temps est une bibliothèque utile pour calculer le temps nécessaire à l'exécution de morceaux de code. Vous pouvez utiliser temps comparer le temps d'exécution de carte(), pour boucles et compréhensions de liste:

>>>

>>> importation Aléatoire
>>> importation temps
>>> TAUX D'IMPOSITION = .08
>>> txns = [[[[Aléatoire.randrange(100) pour _ dans intervalle(100000)]
>>> def get_price(txn):
...     revenir txn * (1 + TAUX D'IMPOSITION)
...
>>> def get_prices_with_map():
...     revenir liste(carte(get_price, txns))
...
>>> def get_prices_with_comprehension():
...     revenir [[[[get_price(txn) pour txn dans txns]
...
>>> def get_prices_with_loop():
...     des prix = []
...     pour txn dans txns:
...         des prix.ajouter(get_price(txn))
...     revenir des prix
...
>>> temps.temps(get_prices_with_map, nombre=100)
2.0554370979998566
>>> temps.temps(get_prices_with_comprehension, nombre=100)
2.3982384680002724
>>> temps.temps(get_prices_with_loop, nombre=100)
3.0531821520007725

Ici, vous définissez trois méthodes qui utilisent chacune une approche différente pour créer une liste. Ensuite, vous dites temps pour exécuter chacune de ces fonctions 100 fois chacune. temps renvoie le temps total qu'il a fallu pour exécuter ces 100 exécutions.

Comme le montre le code, la plus grande différence réside entre l’approche basée sur la boucle et carte(), la boucle prenant 50% plus de temps à exécuter. Que cela compte ou non dépend des besoins de votre application.

Conclusion

Dans ce tutoriel, vous avez appris à utiliser un compréhension de la liste en Python pour accomplir des tâches complexes sans compliquer votre code.

Maintenant vous pouvez:

  • Simplifier les boucles et carte() appels avec déclaratif comprendre la liste
  • Augmentez vos compréhensions avec logique conditionnelle
  • Créer ensemble et dictionnaire les compréhensions
  • Déterminer quand la clarté du code ou les performances dictent une approche alternative

Lorsque vous devez choisir une méthode de création de liste, essayez plusieurs implémentations et déterminez ce qui est le plus facile à lire et à comprendre dans votre scénario spécifique. Si les performances sont importantes, vous pouvez utiliser des outils de profilage pour vous fournir des données exploitables au lieu de vous fier à des intuitions ou à des suppositions sur ce qui fonctionne le mieux.

N'oubliez pas que, même si la compréhension de la liste Python suscite beaucoup d'attention, votre intuition et votre capacité à utiliser les données quand cela compte vous aideront à rédiger du code propre à la tâche à accomplir. C’est finalement la clé pour rendre votre code Pythonic!