Compréhension de la liste et carte

Salut, Habr. Souvent lorsque l'on travaille avec des sĂ©quences, la question se pose de leur crĂ©ation. Il semble ĂȘtre habituĂ© Ă  utiliser l' inclusion de liste (comprĂ©hension de liste) et, dans les livres, Ă  crier sur l'utilisation obligatoire de la fonction de carte intĂ©grĂ©e.

Dans cet article, nous examinerons ces approches pour travailler avec des séquences, comparer les performances et également déterminer dans quelles situations quelle approche est la meilleure.

image

Compréhension des listes


L'inclusion de liste est un mécanisme de génération de liste intégré à Python. Il n'a qu'une seule tùche - construire une liste. L'inclusion de liste crée une liste à partir de n'importe quel type itérable, transformant (filtrant) les valeurs entrantes.

Un exemple d'inclusion de liste simple pour générer une liste de carrés de nombres de 0 à 9:

squares = [x*x for x in range(10)] 

RĂ©sultat:

 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

carte


map est une fonction intégrée au langage. Il accepte une fonction comme premier paramÚtre et un objet itérable comme deuxiÚme. Renvoie un générateur (Python 3.x) ou une liste (Python 2.x). Je choisirai Python 3.

Un exemple d'appel de la fonction map pour générer une liste de carrés de nombres de 0 à 9:

 squares = list(map(lambda x: x*x, range(10))) 

RĂ©sultat:

 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

Comparaison des performances


Construire sans fonctions


À titre expĂ©rimental, nous considĂ©rerons les carrĂ©s des nombres de l'intervalle de 0 Ă  9 999 999:

 python -m timeit -r 10 "[x*x for x in range(10000000)]" python -m timeit -r 10 "list(map(lambda x: x*x, range(10000000)))" 

RĂ©sultats:

 1 loop, best of 10: 833 msec per loop 1 loop, best of 10: 1.22 sec per loop 

Comme vous pouvez le voir, la méthode de compréhension de liste fonctionne environ 32% plus rapidement. Une fois démonté, il n'est pas possible d'obtenir des réponses complÚtes, car la fonction de carte «semble cacher les détails de son travail». Mais cela est probablement dû à l'appel constant de la fonction lambda, à l'intérieur duquel des calculs carrés sont déjà en cours. Dans le cas de la compréhension de liste, il suffit de calculer le carré.

Construire avec des fonctionnalités


À titre de comparaison, nous considĂ©rerons Ă©galement les carrĂ©s des nombres, mais les calculs seront dĂ©sormais Ă  l'intĂ©rieur de la fonction:

 python -m timeit -r 10 -s "def pow2(x): return x*x" "[pow2(x) for x in range(10000000)]" python -m timeit -r 10 -s "def pow2(x): return x*x" "list(map(pow2, range(10000000)))" 

RĂ©sultats:

 1 loop, best of 10: 1.41 sec per loop 1 loop, best of 10: 1.21 sec per loop 

Cette fois, la situation est inversĂ©e. La mĂ©thode de la carte Ă©tait 14% plus rapide. Dans cet exemple, les deux mĂ©thodes sont dans la mĂȘme situation. Les deux doivent appeler une fonction pour calculer le carrĂ©. Cependant, les optimisations internes de la fonction de carte lui permettent d'afficher de meilleurs rĂ©sultats.

Que choisir?


Voici la rÚgle pour choisir la bonne méthode:

image

Il peut y avoir des exceptions Ă  cette rĂšgle, mais dans la plupart des cas, cela vous aidera Ă  faire le bon choix!

la carte est-elle "plus sûre"?


Pourquoi beaucoup recommandent l'utilisation de la carte . Le fait est que dans certains cas, la carte est en fait plus sûre que la compréhension de liste.

Par exemple:

 symbols = ['a', 'b', 'c'] values = [1, 2, 3] for x in symbols: print(x) squared = [x*x for x in values] #  . "x"   "for"  print(x) 

Le résultat du programme sera le suivant:

 a 3 b 3 c 3 

Maintenant, rĂ©Ă©crivez le mĂȘme code en utilisant la carte :

 symbols = ['a', 'b', 'c'] values = [1, 2, 3] for x in symbols: print(x) squared = map(lambda x: x*x, values) #  ,     print(x) 

Conclusion:

 a a b b c c 

Le plus observateur pouvait déjà remarquer à partir de la syntaxe de l'utilisation de map qu'il s'agit de Python 2. En effet, dans le second python, il y avait un genre similaire de problÚme avec l'écrasement des variables. Cependant, dans Python 3, ce problÚme a été corrigé et n'est plus pertinent.

Les exemples dĂ©crits ci-dessus montreront les mĂȘmes rĂ©sultats. Il peut Ă©galement sembler que c'est une erreur stupide et vous ne ferez jamais une telle erreur, cependant, cela peut se produire lorsque vous transfĂ©rez simplement un bloc de code avec une boucle interne Ă  partir d'un autre bloc. Une telle erreur peut vous coĂ»ter beaucoup de temps et de nerfs pour la rĂ©parer.

Conclusion


La comparaison a montré que chacune des méthodes est bonne dans sa situation.

  • Si vous n'avez pas besoin de toutes les valeurs calculĂ©es Ă  la fois (ou peut-ĂȘtre qu'elles ne sont pas du tout nĂ©cessaires), vous devez opter pour la carte . Ainsi, selon les besoins, vous demanderez une partie des donnĂ©es au gĂ©nĂ©rateur, tout en Ă©conomisant une grande quantitĂ© de mĂ©moire (Python 3. En Python 2, cela n'a pas de sens, car la carte renvoie une liste).
  • Si vous devez calculer toutes les valeurs Ă  la fois et que les calculs peuvent ĂȘtre effectuĂ©s sans utiliser de fonctions, vous devez alors faire un choix dans le sens de la comprĂ©hension de la liste . Comme le montrent les rĂ©sultats des expĂ©riences - il prĂ©sente un avantage significatif en termes de performances.

PS: Si j'ai raté quelque chose, je suis heureux d'en discuter avec vous dans les commentaires.

Source: https://habr.com/ru/post/fr479252/


All Articles