trouver un expert Python
Python réduire()
est une fonction qui met en œuvre une technique mathématique appelée pliant ou réduction. réduire()
est utile lorsque vous devez appliquer une fonction à un itérable et la réduire à une seule valeur cumulative. Python réduire()
est populaire parmi les développeurs programmation fonctionnelle fond, mais Python a plus à offrir.
Dans ce didacticiel, vous découvrirez comment réduire()
fonctionne et comment l'utiliser efficacement. Vous couvrirez également quelques outils Python alternatifs qui peuvent être plus Pythonic, lisibles et efficaces que réduire()
.
Grâce à ces connaissances, vous pourrez décider des outils à utiliser pour résoudre les problèmes de réduction ou de pliage en Python.
Pour une meilleure compréhension de Python réduire()
, il serait utile d'avoir une certaine connaissance préalable de la façon de travailler avec les itérables Python, en particulier comment les parcourir en boucle à l'aide d'un pour
boucle.
Explorer la programmation fonctionnelle en Python
La programmation fonctionnelle est un paradigme de programmation basé sur la décomposition d'un problème en un ensemble de fonctions individuelles. Idéalement, chaque fonction ne prend qu'un ensemble d'arguments d'entrée et produit une sortie.
Dans la programmation fonctionnelle, les fonctions n'ont aucun état interne qui affecte la sortie qu'elles produisent pour une entrée donnée. Cela signifie que chaque fois que vous appelez une fonction avec le même ensemble d'arguments d'entrée, vous obtiendrez le même résultat ou la même sortie.
Dans un programme fonctionnel, les données d'entrée transitent par un ensemble de fonctions. Chaque fonction opère sur son entrée et produit une sortie. La programmation fonctionnelle essaie d'éviter autant que possible les types de données mutables et les changements d'état. Il fonctionne avec les données qui circulent entre les fonctions.
Les autres caractéristiques essentielles de la programmation fonctionnelle sont les suivantes:
- L'utilisation de récursivité plutôt que des boucles ou d'autres structures comme structure de contrôle de flux primaire
- Un focus sur le traitement des listes ou des tableaux
- Un focus sur quelle doit être calculé plutôt que sur Comment pour le calculer
- L'utilisation de fonctions pures qui évitent Effets secondaires
- L'utilisation de fonctions d'ordre supérieur
Il existe plusieurs concepts importants dans cette liste. Voici un aperçu de certains d'entre eux:
-
Récursivité est une technique dans laquelle les fonctions s'appellent, directement ou indirectement, pour boucler. Il permet à un programme de boucler sur des structures de données dont la longueur est inconnue ou imprévisible.
-
Fonctions pures sont des fonctions qui n'ont aucun effet secondaire. En d'autres termes, ce sont des fonctions qui ne mettent à jour ni ne modifient aucune variable globale, aucun objet ou structure de données dans le programme. Ces fonctions produisent une sortie qui ne dépend que de l'entrée, ce qui est plus proche du concept de fonction mathématique.
-
Fonctions d'ordre supérieur sont des fonctions qui opèrent sur d'autres fonctions en prenant des fonctions comme arguments, en retournant des fonctions, ou les deux, comme avec les décorateurs Python.
Étant donné que Python est un langage de programmation multi-paradigmes, il fournit certains outils qui prennent en charge un style de programmation fonctionnel:
Même si Python n'est pas fortement influencé par les langages de programmation fonctionnels, en 1993, il y avait une demande claire pour certaines des fonctionnalités de programmation fonctionnelle répertoriées ci-dessus.
En réponse, plusieurs outils fonctionnels ont été ajoutés au langage. Selon Guido van Rossum, ils ont été fournis par un membre de la communauté:
Acquisition de Python
lambda
,réduire()
,filtre()
etcarte()
, gracieuseté (je crois) d'un pirate informatique Lisp qui les a manqués et a soumis des correctifs de travail. (La source)
Au fil des ans, de nouvelles fonctionnalités telles que les listes de compréhension, les expressions de générateur et les fonctions intégrées comme somme()
, min ()
, max ()
, tout()
, et tout()
ont été considérés comme des remplacements pythoniques pour carte()
, filtre()
, et réduire()
. Guido a prévu de supprimer carte()
, filtre()
, réduire()
, et même lambda
à partir du langage en Python 3.
Heureusement, cette suppression n'a pas pris effet, principalement parce que la communauté Python ne voulait pas abandonner ces fonctionnalités populaires. Ils sont toujours présents et largement utilisés par les développeurs ayant une solide expérience en programmation fonctionnelle.
Dans ce didacticiel, vous découvrirez comment utiliser Python réduire()
pour traiter les itérables et les réduire à une seule valeur cumulative sans utiliser de pour
boucle. Vous découvrirez également certains outils Python que vous pouvez utiliser à la place de réduire()
pour rendre votre code plus Pythonic, lisible et efficace.
Premiers pas avec Python réduire()
Python réduire()
met en œuvre une technique mathématique communément appelée pliant ou réduction. Vous effectuez un pli ou une réduction lorsque vous réduisez une liste d'éléments à une seule valeur cumulée. Python réduire()
fonctionne sur n'importe quel itérable – pas seulement sur les listes – et effectue les étapes suivantes:
- Appliquer une fonction (ou appelable) aux deux premiers éléments dans un itérable et générer un résultat partiel.
- Utilisation ce résultat partiel, avec le troisième élément de l'itérable, pour générer un autre résultat partiel.
- Répéter le processus jusqu'à ce que l'itérable soit épuisé, puis renvoie une seule valeur cumulative.
L'idée derrière Python réduire()
consiste à prendre une fonction existante, à l'appliquer cumulativement à tous les éléments d'un itérable et à générer une seule valeur finale. En général, Python réduire()
est pratique pour traiter les itérables sans écrire explicitement pour
boucles. Puisque réduire()
est écrit en C, sa boucle interne peut être plus rapide qu'un Python explicite pour
boucle.
Python réduire()
était à l'origine une fonction intégrée (et est toujours en Python 2.x), mais elle a été déplacée vers functools.reduce ()
en Python 3.0. Cette décision était basée sur certains problèmes de performances et de lisibilité possibles.
Une autre raison de déménager réduire()
à functools
était l'introduction de fonctions intégrées comme somme()
, tout()
, tout()
, max ()
, min ()
, et len ()
, qui offrent des moyens plus efficaces, lisibles et pythoniques de traiter les cas d’utilisation courants réduire()
. Vous apprendrez à les utiliser à la place de réduire()
plus loin dans le didacticiel.
Dans Python 3.x, si vous devez utiliser réduire()
, vous devez d'abord importer la fonction dans votre portée actuelle à l'aide d'un importer
de l'une des manières suivantes:
import functools
puis utilisez des noms complets commefunctools.reduce ()
.de functools import réduire
puis appelerréduire()
directement.
Selon la documentation de réduire()
, la fonction a la signature suivante:
functools.réduire(fonction, itérable[[[[ initialiseur])
La documentation Python indique également que réduire()
est à peu près équivalent à la fonction Python suivante:
def réduire(fonction, itérable, initialiseur=Aucun):
il = iter(itérable)
si initialiseur est Aucun:
valeur = suivant(il)
autre:
valeur = initialiseur
pour élément dans il:
valeur = fonction(valeur, élément)
revenir valeur
Comme cette fonction Python, réduire()
fonctionne en appliquant une fonction à deux arguments aux éléments de itérable
en boucle de gauche à droite, réduisant finalement itérable
à un seul cumul valeur
.
Python réduire()
accepte également un troisième argument facultatif appelé initialiseur
qui fournit une valeur de départ au calcul ou à la réduction.
Dans les deux sections suivantes, vous découvrirez en détail comment Python réduire()
œuvres et le sens derrière chacun de ses arguments.
Les arguments requis: fonction
et itérable
Le premier argument de Python réduire()
est une fonction à deux arguments appelée commodément fonction
. Cette fonction sera appliquée aux éléments dans un itérable pour calculer cumulativement une valeur finale.
Même si la documentation officielle fait référence au premier argument de réduire()
comme "une fonction de deux arguments", vous pouvez passer n'importe quel Python appelable à réduire()
tant que l'appelable accepte deux arguments. Les objets appelables incluent des classes, des instances qui implémentent une méthode spéciale appelée __appel__()
, méthodes d'instance, méthodes de classe, méthodes statiques et fonctions.
Remarque: Pour plus de détails sur les objets appelables Python, vous pouvez consulter la documentation Python et faire défiler jusqu'à «Types appelables».
Le deuxième argument requis, itérable
, acceptera tout Python itérable, comme son nom l'indique. Cela inclut les listes, les tuples, gamme
objets, générateurs, itérateurs, ensembles, clés et valeurs de dictionnaire et tout autre objet Python sur lequel vous pouvez effectuer une itération.
Remarque: Si vous passez un itérateur à Python réduire()
, la fonction devra alors épuiser l'itérateur avant d'obtenir une valeur finale. Donc, l'itérateur à portée de main ne restera pas paresseux.
Pour comprendre comment réduire()
fonctionne, vous allez écrire une fonction qui calcule la somme de deux nombres et imprime l'opération mathématique équivalente à l'écran. Voici le code:
>>> def my_add(une, b):
... résultat = une + b
... impression(F"une + b = résultat")
... revenir résultat
Cette fonction calcule la somme de une
et b
, imprime un message avec l'opération à l'aide d'une chaîne f et renvoie le résultat du calcul. Voici comment ça fonctionne:
>>> my_add(5, 5)
5 + 5 = 10
dix
my_add ()
est une fonction à deux arguments, vous pouvez donc la transmettre à Python réduire()
avec un itérable pour calculer la somme cumulée des éléments de l'itérable. Découvrez le code suivant qui utilise une liste de nombres:
>>> de functools importer réduire
>>> Nombres = [[[[0, 1, 2, 3, 4]
>>> réduire(my_add, Nombres)
0 + 1 = 1
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
dix
Quand vous appelez réduire()
, qui passe my_add ()
et Nombres
comme arguments, vous obtenez une sortie qui montre toutes les opérations réduire()
effectue pour arriver à un résultat final de dix
. Dans ce cas, les opérations sont équivalentes à ((((0 + 1) + 2) + 3) + 4) = 10
.
L'appel à réduire()
dans l'exemple ci-dessus s'applique my_add ()
aux deux premiers éléments de Nombres
(0
et 1
) et obtient 1
comme résultat. ensuite réduire()
appels my_add ()
en utilisant 1
et l'élément suivant dans Nombres
(lequel est 2
) comme arguments, obtention 3
comme résultat. Le processus est répété jusqu'à Nombres
manque d'articles et réduire()
renvoie un résultat final de dix
.
L'argument facultatif: initialiseur
Le troisième argument de Python réduire()
, appelé initialiseur
, est facultative. Si vous fournissez une valeur à initialiseur
, puis réduire()
va le nourrir au premier appel de fonction
comme premier argument.
Cela signifie que le premier appel à fonction
utilisera la valeur de initialiseur
et le premier élément de itérable
pour effectuer son premier calcul partiel. Après ça, réduire()
continue de travailler avec les éléments suivants de itérable
.
Voici un exemple dans lequel vous utilisez my_add ()
avec initialiseur
mis à 100
:
>>> de functools importer réduire
>>> Nombres = [[[[0, 1, 2, 3, 4]
>>> réduire(my_add, Nombres, 100)
100 + 0 = 100
100 + 1 = 101
101 + 2 = 103
103 + 3 = 106
106 + 4 = 110
110
Puisque vous fournissez une valeur de 100
à initialiseur
, Python réduire()
utilise cette valeur dans le premier appel comme premier argument pour my_add ()
. Notez que dans la première itération, my_add ()
les usages 100
et 0
, qui est le premier élément de Nombres
, pour effectuer le calcul 100 + 0 = 100
.
Un autre point à noter est que si vous fournissez une valeur à initialiseur
, puis réduire()
effectuera une itération de plus qu'il le ferait sans un initialiseur
.
Si vous prévoyez d'utiliser réduire()
pour traiter les itérables qui peuvent être vides, il est recommandé de fournir une valeur à initialiseur
. Python réduire()
utilisera cette valeur comme valeur de retour par défaut lorsque itérable
est vide. Si vous ne fournissez pas de initialiseur
valeur, réduire()
soulèvera un Erreur-type
. Jetez un œil à l'exemple suivant:
>>> de functools importer réduire
>>> # Utilisation d'une valeur d'initialisation
>>> réduire(my_add, [], 0) # Utilisez 0 comme valeur de retour
0
>>> # N'utiliser aucune valeur d'initialisation
>>> réduire(my_add, []) # Lève une TypeError avec un itérable vide
Traceback (dernier appel le plus récent):
Fichier "" , ligne 1, dans
Erreur-type: réduire () de séquence vide sans valeur initiale
Si vous appelez réduire()
avec un vide itérable
, alors la fonction renverra la valeur fournie à initialiseur
. Si vous ne fournissez pas de initialiseur
, puis réduire()
soulèvera un Erreur-type
lors du traitement des itérables vides.
Maintenant que vous savez comment réduire()
fonctionne, vous êtes prêt à apprendre à l'appliquer à certains problèmes de programmation courants.
Réduire les Iterables avec Python réduire()
Jusqu'à présent, vous avez appris comment Python réduire()
fonctionne et comment l'utiliser pour réduire les itérables à l'aide d'une fonction définie par l'utilisateur. Vous avez également appris la signification de chaque argument réduire()
et comment ils fonctionnent.
Dans cette section, vous examinerez quelques cas d'utilisation courants pour réduire()
et comment les résoudre à l'aide de la fonction. Vous découvrirez également quelques outils Python alternatifs que vous pouvez utiliser à la place de réduire()
pour rendre votre code plus Pythonic, efficace et lisible.
Addition de valeurs numériques
le "Bonjour le monde!"
de Python réduire()
est le cas d'utilisation somme. Il s'agit de calculer la somme cumulée d'une liste de nombres. Disons que vous avez une liste de nombres comme [1, 2, 3, 4]
. Sa somme sera 1 + 2 + 3 + 4 = 10
. Voici un exemple rapide de la façon de résoudre ce problème à l'aide d'un Python pour
boucle:
>>> Nombres = [[[[1, 2, 3, 4]
>>> total = 0
>>> pour num dans Nombres:
... total + = num
...
>>> total
dix
le pour
boucle itère sur chaque valeur Nombres
et les accumule dans total
. Le résultat final est la somme de toutes les valeurs, qui dans cet exemple est dix
. Une variable utilisée comme total
dans cet exemple est parfois appelé accumulateur.
Il s'agit sans doute du cas d'utilisation le plus courant pour Python. réduire()
. Pour implémenter cette opération avec réduire()
, vous avez plusieurs options. Certains d'entre eux incluent l'utilisation réduire()
avec l'une des fonctions suivantes:
Pour utiliser une fonction définie par l'utilisateur, vous devez coder une fonction qui ajoute deux nombres. Ensuite, vous pouvez utiliser cette fonction avec réduire()
. Pour cet exemple, vous pouvez réécrire my_add ()
comme suit:
>>> def my_add(une, b):
... revenir une + b
...
>>> my_add(1, 2)
3
my_add ()
ajoute deux nombres, une
et b
et renvoie le résultat. Avec my_add ()
en place, vous pouvez utiliser réduire()
pour calculer la somme des valeurs dans un itérable Python. Voici comment:
>>> de functools importer réduire
>>> Nombres = [[[[1, 2, 3, 4]
>>> réduire(my_add, Nombres)
dix
L'appel à réduire()
s'applique my_add ()
aux articles de Nombres
pour calculer leur somme cumulée. Le résultat final est dix
, comme prévu.
Vous pouvez également effectuer le même calcul en utilisant un lambda
fonction. Dans ce cas, vous avez besoin d'un lambda
fonction qui prend deux nombres comme arguments et renvoie leur somme. Jetez un œil à l'exemple suivant:
>>> de functools importer réduire
>>> Nombres = [[[[1, 2, 3, 4]
>>> réduire(lambda une, b: une + b, Nombres)
dix
le lambda
La fonction prend deux arguments et retourne leur somme. réduire()
applique le lambda
fonctionner en boucle pour calculer la somme cumulée des éléments Nombres
.
De même, vous pouvez profiter d'un module Python appelé opérateur
. Ce module exporte un tas de fonctions qui correspondent aux opérateurs intrinsèques de Python. Pour le problème à résoudre, vous pouvez utiliser operator.add ()
avec Python réduire()
. Découvrez l'exemple suivant:
>>> de opérateur importer ajouter
>>> de functools importer réduire
>>> ajouter(1, 2)
3
>>> Nombres = [[[[1, 2, 3, 4]
>>> réduire(ajouter, Nombres)
dix
Dans cet exemple, ajouter()
prend deux arguments et retourne leur somme. Vous pouvez donc utiliser ajouter()
avec réduire()
pour calculer la somme de tous les éléments de Nombres
. Puisque ajouter()
est écrit en C et optimisé pour l'efficacité, il peut être votre meilleur choix lors de l'utilisation réduire()
pour résoudre le cas d'utilisation de somme. Notez que l'utilisation de operator.add ()
est également plus lisible que l’utilisation d’un lambda
fonction.
Le cas d'utilisation somme est si commun en programmation que Python, depuis la version 2.3, a inclus une fonction intégrée dédiée, somme()
, pour le résoudre. somme()
est déclaré comme somme (itérable[, start])
.
début
est un argument facultatif pour somme()
et par défaut à 0
. La fonction ajoute la valeur de début
aux articles de itérable
de gauche à droite et renvoie le total. Jetez un œil à l'exemple suivant:
>>> Nombres = [[[[1, 2, 3, 4]
>>> somme(Nombres)
dix
Puisque somme()
est une fonction intégrée, vous n'avez pas besoin d'importer quoi que ce soit. Il est toujours disponible pour vous. En utilisant somme()
est la façon la plus Pythonique de résoudre le cas d'utilisation de somme. C'est propre, lisible et concis. Il suit un principe de base de Python:
Simple, c'est mieux que complexe. (La source)
L'addition de somme()
à la langue a été une grande victoire en termes de lisibilité et de performances par rapport à l'utilisation réduire()
ou un pour
boucle.
Remarque: Pour plus de détails sur la comparaison des performances de réduire()
avec les performances des autres outils de réduction Python, consultez la section La performance est la clé.
Si vous traitez avec le cas d'utilisation de somme, les bonnes pratiques recommandent l'utilisation de somme()
.
Multiplication des valeurs numériques
le cas d'utilisation du produit de Python réduire()
est assez similaire au cas d'utilisation de la somme, mais cette fois, l'opération est la multiplication. En d'autres termes, vous devez calculer le produit de toutes les valeurs dans un itérable.
Par exemple, disons que vous avez la liste [1, 2, 3, 4]
. Son produit sera 1 * 2 * 3 * 4 = 24
. Vous pouvez calculer cela en utilisant un Python pour
boucle. Découvrez l'exemple suivant:
>>> Nombres = [[[[1, 2, 3, 4]
>>> produit = 1
>>> pour num dans Nombres:
... produit * = num
...
>>> produit
24
La boucle parcourt les éléments de Nombres
, multipliant chaque élément par le résultat de l'itération précédente. Dans ce cas, la valeur de départ de l'accumulateur produit
devrait être 1
au lieu de 0
. Étant donné que tout nombre multiplié par zéro est égal à zéro, une valeur de départ de 0
rendra toujours votre produit égal à 0
.
Ce calcul est également un cas d'utilisation très populaire pour Python réduire()
. Encore une fois, vous découvrirez trois façons de résoudre le problème. Vous utiliserez réduire()
avec:
- Une fonction définie par l'utilisateur
- UNE
lambda
fonction - Une fonction appelée
operator.mul ()
Pour l'option 1, vous devrez coder une fonction personnalisée qui prend deux arguments et renvoie leur produit. Ensuite, vous utiliserez cette fonction avec réduire()
pour calculer le produit des articles dans un itérable. Jetez un œil au code suivant:
>>> de functools importer réduire
>>> def my_prod(une, b):
... revenir une * b
...
>>> my_prod(1, 2)
2
>>> Nombres = [[[[1, 2, 3, 4]
>>> réduire(my_prod, Nombres)
24
La fonction my_prod ()
multiplie deux nombres, une
et b
. L'appel à réduire()
itère sur les éléments de Nombres
et calcule leur produit en appliquant my_prod ()
aux éléments successifs. Le résultat final est le produit de tous les articles Nombres
, qui dans cet exemple est 24
.
Si vous préférez utiliser un lambda
pour résoudre ce cas d'utilisation, alors vous avez besoin d'une fonction qui prend deux arguments et renvoie leur produit. Voici un exemple:
>>> de functools importer réduire
>>> Nombres = [[[[1, 2, 3, 4]
>>> réduire(lambda une, b: une * b, Nombres)
24
La fonction anonyme fait la magie en multipliant les éléments successifs tout en réduire()
itère sur Nombres
. Encore une fois, le résultat est le produit de tous les articles Nombres
.
Vous pouvez aussi utiliser operator.mul ()
pour aborder le cas d'utilisation du produit. operator.mul ()
prend deux nombres et renvoie le résultat de leur multiplication. Il s'agit de la bonne fonctionnalité pour résoudre le problème actuel. Découvrez l'exemple suivant:
>>> de opérateur importer mul
>>> de functools importer réduire
>>> mul(2, 2)
4
>>> Nombres = [[[[1, 2, 3, 4]
>>> réduire(mul, Nombres)
24
Puisque mul ()
est hautement optimisé, votre code fonctionnera mieux si vous utilisez cette fonction plutôt qu'une fonction définie par l'utilisateur ou un lambda
fonction. Notez que cette solution est également beaucoup plus lisible.
Enfin, si vous utilisez Python 3.8, vous avez alors accès à une solution plus pythonique et lisible pour ce cas d'utilisation. Python 3.8 a ajouté une nouvelle fonction appelée prod ()
, qui vit dans le Python math
module. Cette fonction est analogue à somme()
mais renvoie le produit d'un début
valeur multipliée par un itérable
de nombres.
Dans le cas de math.prod ()
, l'argument début
est facultatif et par défaut 1
. Voici comment ça fonctionne:
>>> de math importer prod
>>> Nombres = [[[[1, 2, 3, 4]
>>> prod(Nombres)
24
C'est également une grande victoire en termes de lisibilité et d'efficacité par rapport à l'utilisation réduire()
. Donc, si vous utilisez Python 3.8 et que la réduction de produit est une opération courante dans votre code, vous serez mieux servi en utilisant math.prod ()
plutôt que de Python réduire()
.
Recherche de la valeur minimale et maximale
Le problème de trouver le minimum et le maximum La valeur dans un itérable est également un problème de réduction que vous pouvez résoudre à l'aide de Python. réduire()
. L'idée est de comparer les éléments de l'itérable pour trouver la valeur minimale ou maximale.
Disons que vous avez la liste des numéros [3, 5, 2, 4, 7, 1]
. Dans cette liste, la valeur minimale est 1
et la valeur maximale est 7
. Pour trouver ces valeurs, vous pouvez utiliser un Python pour
boucle. Découvrez le code suivant:
>>> Nombres = [[[[3, 5, 2, 4, 7, 1]
>>> # Le minimum
>>> valeur_min, *du repos = Nombres
>>> pour num dans du repos:
... si num < valeur_min:
... valeur_min = num
...
>>> valeur_min
1
>>> # Maximum
>>> Valeur max, *du repos = Nombres
>>> pour num dans du repos:
... si num > Valeur max:
... Valeur max = num
...
>>> Valeur max
7
Les deux boucles parcourent les éléments de du repos
et mettre à jour la valeur de valeur_min
ou Valeur max
selon le résultat de comparaisons successives. Notez qu'au départ, valeur_min
et Valeur max
tenir le numéro 3
, qui est la première valeur de Nombres
. La variable du repos
contient les valeurs restantes dans Nombres
. En d'autres termes, reste = [5, 2, 4, 7, 1]
.
Remarque: Dans les exemples ci-dessus, vous utilisez l'opérateur de décompression itérable Python (*
) à déballer ou développez les valeurs dans Nombres
en deux variables. Dans le premier cas, l'effet net est que valeur_min
obtient la première valeur Nombres
, lequel est 3
, et du repos
recueille les valeurs restantes dans une liste.
Découvrez les détails dans les exemples suivants:
>>> Nombres = [[[[3, 5, 2, 4, 7, 1]
>>> valeur_min, *du repos = Nombres
>>> valeur_min
3
>>> du repos
[5, 2, 4, 7, 1]
>>> Valeur max, *du repos = Nombres
>>> Valeur max
3
>>> du repos
[5, 2, 4, 7, 1]
L'opérateur de déballage itérable Python (*
) est utile lorsque vous devez décompresser une séquence ou itérable en plusieurs variables.
Pour une meilleure compréhension des opérations de déballage en Python, vous pouvez consulter PEP 3132 Extended Iterable Unpacking et PEP 448 Additional Unpacking Generalizations.
Maintenant, réfléchissez à la façon dont vous pouvez trouver la valeur minimale et maximale dans un itérable à l'aide de Python. réduire()
. Encore une fois, vous pouvez utiliser une fonction définie par l'utilisateur ou un lambda
fonction de vos besoins.
Le code suivant implémente une solution qui utilise deux fonctions définies par l'utilisateur différentes. La première fonction prendra deux arguments, une
et b
et renvoyez leur minimum. La deuxième fonction utilisera un processus similaire, mais elle renverra la valeur maximale.
Voici les fonctions et comment les utiliser avec Python réduire()
pour trouver la valeur minimale et maximale dans un itérable:
>>> de functools importer réduire
>>> # Le minimum
>>> def my_min_func(une, b):
... revenir une si une < b autre b
...
>>> # Maximum
>>> def my_max_func(une, b):
... revenir une si une > b autre b
...
>>> Nombres = [[[[3, 5, 2, 4, 7, 1]
>>> réduire(my_min_func, Nombres)
1
>>> réduire(my_max_func, Nombres)
7
Quand tu cours réduire()
avec my_min_func ()
et my_max_func ()
, vous obtenez la valeur minimale et maximale dans Nombres
, respectivement. réduire()
itère sur les éléments de Nombres
, les compare par paires cumulatives et renvoie finalement la valeur minimale ou maximale.
Remarque: Implémenter my_min_func ()
et my_max_func ()
, vous avez utilisé une instruction conditionnelle Python, ou opérateur ternaire, comme revenir
valeur. Pour approfondir ce que sont les instructions conditionnelles et comment elles fonctionnent, consultez les instructions conditionnelles en Python (if / elif / else).
Vous pouvez également utiliser un lambda
fonction pour résoudre le problème minimum et maximum. Jetez un œil aux exemples suivants:
>>> de functools importer réduire
>>> Nombres = [[[[3, 5, 2, 4, 7, 1]
>>> # Le minimum
>>> réduire(lambda une, b: une si une < b autre b, Nombres)
1
>>> # Maximum
>>> réduire(lambda une, b: une si une > b autre b, Nombres)
7
Cette fois, vous utilisez deux lambda
fonctions qui découvrent si une
est inférieur ou supérieur à b
. Dans ce cas, Python réduire()
applique le lambda
fonction à chaque valeur Nombres
, en le comparant avec le résultat du calcul précédent. À la fin du processus, vous obtenez la valeur minimale ou maximale.
Le problème minimum et maximum est si commun en programmation que Python a ajouté des fonctions intégrées pour effectuer ces réductions. Ces fonctions sont appelées commodément min ()
et max ()
et vous n'avez pas besoin d'importer quoi que ce soit pour pouvoir les utiliser. Voici comment ils fonctionnent:
>>> Nombres = [[[[3, 5, 2, 4, 7, 1]
>>> min(Nombres)
1
>>> max(Nombres)
7
Lorsque vous utilisez min ()
et max ()
pour trouver l'élément minimum et maximum dans un itérable, votre code est bien plus lisible par rapport à l'utilisation de Python réduire()
. De plus, depuis min ()
et max ()
sont des fonctions C hautement optimisées, vous pouvez également dire que votre code sera plus efficace.
Donc, quand il s'agit de résoudre ce problème en Python, il est préférable d'utiliser min ()
et max ()
plutôt que réduire()
.
Vérifier si toutes les valeurs sont vraies
le cas d'utilisation tout à fait vrai de Python réduire()
implique de savoir si tous les éléments d'un itérable sont vrais ou non. Pour résoudre ce problème, vous pouvez utiliser réduire()
avec une fonction définie par l'utilisateur ou un lambda
fonction.
Vous allez commencer par coder un pour
boucle pour savoir si tous les éléments d'un itérable sont vrais. Voici le code:
>>> def check_all_true(itérable):
... pour article dans itérable:
... si ne pas article:
... revenir Faux
... revenir Vrai
...
>>> check_all_true([[[[1, 1, 1, 1, 1])
Vrai
>>> check_all_true([[[[1, 1, 1, 1, 0])
Faux
>>> check_all_true([])
Vrai
Si toutes les valeurs de itérable
sont vrais, alors check_all_true ()
Retour Vrai
. Sinon, il revient Faux
. Il revient également Vrai
avec des itérables vides. check_all_true ()
met en œuvre un évaluation des courts-circuits. Cela signifie que la fonction retourne dès qu'elle trouve une fausse valeur sans traiter le reste des éléments dans itérable
.
Pour résoudre ce problème à l'aide de Python réduire()
, vous devrez écrire une fonction qui accepte deux arguments et renvoie Vrai
si les deux arguments sont vrais. Si un ou les deux arguments sont faux, la fonction renverra Faux
. Voici le code:
>>> def both_true(une, b):
... revenir bool(une et b)
...
>>> both_true(1, 1)
Vrai
>>> both_true(1, 0)
Faux
>>> both_true(0, 0)
Faux
Cette fonction prend deux arguments, une
et b
. Ensuite, vous utilisez le et
pour tester si les deux arguments sont vrais. La valeur de retour sera Vrai
si les deux arguments sont vrais. Sinon, ce sera Faux
.
En Python, les objets suivants sont considérés comme faux:
- Des constantes comme
Aucun
etFaux
- Types numériques avec une valeur nulle comme
0
,0,0
,0j
,Décimal (0)
, etFraction (0, 1)
- Séquences vides et collections comme
""
,()
,[]
,,
ensemble()
, etplage (0)
- Objets qui implémentent
__bool __ ()
avec une valeur de retour deFaux
ou__len __ ()
avec une valeur de retour de0
Tout autre objet sera considéré comme vrai.
Vous devez utiliser bool ()
pour convertir la valeur de retour de et
soit dans Vrai
ou Faux
. Si vous n'utilisez pas bool ()
, votre fonction ne se comportera pas comme prévu, car et
renvoie l'un des objets de l'expression au lieu de Vrai
ou Faux
. Découvrez les exemples suivants:
>>> une = 0
>>> b = 1
>>> une et b
0
>>> une = 1
>>> b = 2
>>> une et b
2
et
renvoie la première valeur de l'expression si elle est fausse. Sinon, il renvoie la dernière valeur de l'expression quelle que soit sa valeur de vérité. C’est pourquoi vous devez utiliser bool ()
dans ce cas. bool ()
renvoie la valeur booléenne (Vrai
ou Faux
) résultant de l'évaluation d'une expression booléenne ou d'un objet. Découvrez les exemples en utilisant bool ()
:
>>> une = 0
>>> b = 1
>>> bool(une et b)
Faux
>>> une = 1
>>> b = 2
>>> bool(une et b)
Vrai
bool ()
reviendra toujours soit Vrai
ou Faux
après avoir évalué l'expression ou l'objet à portée de main.
Tu peux passer both_true ()
à réduire()
pour vérifier si tous les éléments d'un itérable sont vrais ou non. Voici comment cela fonctionne:
>>> de functools importer réduire
>>> réduire(both_true, [[[[1, 1, 1, 1, 1])
Vrai
>>> réduire(both_true, [[[[1, 1, 1, 1, 0])
Faux
>>> réduire(both_true, [], Vrai)
Vrai
Si vous passez both_true ()
comme argument pour réduire()
, vous obtiendrez Vrai
si tous les éléments de l'itérable sont vrais. Sinon, vous obtiendrez Faux
.
Dans le troisième exemple, vous passez Vrai
à la initialiseur
de réduire()
pour obtenir le même comportement que check_all_true ()
et pour éviter un Erreur-type
.
Vous pouvez également utiliser un lambda
fonction pour résoudre le cas d'utilisation tout-vrai de réduire()
. Voici quelques exemples:
>>> de functools importer réduire
>>> réduire(lambda une, b: bool(une et b), [[[[0, 0, 1, 0, 0])
Faux
>>> réduire(lambda une, b: bool(une et b), [[[[1, 1, 1, 2, 1])
Vrai
>>> réduire(lambda une, b: bool(une et b), [], Vrai)
Vrai
Ce lambda
la fonction est assez similaire à both_true ()
et utilise la même expression comme valeur de retour. Il revient Vrai
si les deux arguments sont vrais. Sinon, il revient Faux
.
Notez que contrairement à check_all_true ()
, lorsque vous utilisez réduire()
pour résoudre le cas d'utilisation vrai, il n'y a pas d'évaluation de court-circuit car réduire()
ne revient pas tant qu'il n'a pas parcouru l'intégralité de l'itérable. Cela peut ajouter du temps de traitement supplémentaire à votre code.
Par exemple, disons que vous avez la liste lst = [1, 0, 2, 0, 0, 1]
et vous devez vérifier si tous les éléments lst
sont vrai. Dans ce cas, check_all_true ()
se terminera dès que sa boucle traitera la première paire d'éléments (1
et 0
) car 0
c'est faux. Vous n'avez pas besoin de continuer l'itération car vous avez déjà une réponse au problème.
D'un autre côté, le réduire()
La solution ne se terminera pas tant qu'elle n'aura pas traité tous les éléments lst
. Cela fait cinq itérations plus tard. Imaginez maintenant ce que cela ferait pour les performances de votre code si vous traitez un grand itérable!
Heureusement, Python fournit le bon outil pour résoudre le problème tout à fait vrai d'une manière Pythonique, lisible et efficace: la fonction intégrée tout()
.
Vous pouvez utiliser tout (itérable)
pour vérifier si tous les éléments itérable
sont vrai. Voici comment tout()
travaux:
>>> tout([[[[1, 1, 1, 1, 1])
Vrai
>>> tout([[[[1, 1, 1, 0, 1])
Faux
>>> tout([])
Vrai
tout()
boucle sur les éléments dans un itérable, vérifiant la valeur de vérité de chacun d'eux. Si tout()
trouve un faux article, puis il retourne Faux
. Sinon, il revient Vrai
. Si vous appelez tout()
avec un itérable vide, alors vous obtenez Vrai
car il n'y a pas de faux élément dans un itérable vide.
tout()
est une fonction C optimisée pour les performances. Cette fonction est également implémentée à l'aide d'une évaluation de court-circuit. Donc, si vous traitez avec le vrai problème en Python, alors vous devriez envisager d'utiliser tout()
au lieu de réduire()
.
Vérifier si une valeur est vraie
Un autre cas d'utilisation courant pour Python réduire()
est le tout cas d'utilisation vrai. Cette fois, vous devez savoir si au moins un élément d'un itérable est vrai. Pour résoudre ce problème, vous devez écrire une fonction qui prend un itérable et retourne Vrai
si un élément de l'itérable est vrai et Faux
autrement. Jetez un œil à l'implémentation suivante de cette fonction:
>>> def check_any_true(itérable):
... pour article dans itérable:
... si article:
... revenir Vrai
... revenir Faux
...
>>> check_any_true([[[[0, 0, 0, 1, 0])
Vrai
>>> check_any_true([[[[0, 0, 0, 0, 0])
Faux
>>> check_any_true([])
Faux
Si au moins un élément dans itérable
est vrai, alors check_any_true ()
Retour Vrai
. Il revient Faux
seulement si tout les éléments sont faux ou si l'itérable est vide. Cette fonction implémente également une évaluation de court-circuit car elle renvoie dès qu'elle trouve une vraie valeur, le cas échéant.
Pour résoudre ce problème à l'aide de Python réduire()
, vous devez coder une fonction qui prend deux arguments et renvoie Vrai
si au moins l'un d'eux est vrai. Si les deux sont faux, la fonction devrait retourner Faux
.
Voici une implémentation possible pour cette fonction:
>>> def any_true(une, b):
... revenir bool(une ou b)
...
>>> any_true(1, 0)
Vrai
>>> any_true(0, 1)
Vrai
>>> any_true(0, 0)
Faux
any_true ()
Retour Vrai
si au moins l'un de ses arguments est vrai. Si les deux arguments sont faux, alors any_true ()
Retour Faux
. Comme avec both_true ()
dans la section ci-dessus, any_true ()
les usages bool ()
pour convertir le résultat de l'expression a ou B
soit Vrai
ou Faux
.
Le Python ou
l'opérateur fonctionne un peu différemment de et
. Il renvoie le premier vrai objet ou le dernier objet de l'expression. Découvrez les exemples suivants:
>>> une = 1
>>> b = 2
>>> une ou b
1
>>> une = 0
>>> b = 1
>>> une ou b
1
>>> une = 0
>>> b = []
>>> une ou b
[]
Le Python ou
L'opérateur renvoie le premier objet vrai ou, si les deux sont faux, le dernier objet. Donc, vous devez également utiliser bool ()
pour obtenir une valeur de retour cohérente de any_true ()
.
Une fois cette fonction en place, vous pouvez poursuivre la réduction. Jetez un œil aux appels suivants à réduire()
:
>>> de functools importer réduire
>>> réduire(any_true, [[[[0, 0, 0, 1, 0])
Vrai
>>> reduce(any_true, [[[[0, 0, 0, 0, 0])
Faux
>>> reduce(any_true, [], Faux)
Faux
You’ve solved the problem using Python’s reduce()
. Note that in the third example, you pass Faux
to the initializer of reduce()
to reproduce behavior of the original check_any_true()
and also to avoid a TypeError
.
Remarque: Like the examples in the previous section, these examples of reduce()
don’t make a short-circuit evaluation. That means they can affect the performance of your code.
You can also use a lambda
function with reduce()
to solve the any-true use case. Here’s how you can do it:
>>> de functools import reduce
>>> reduce(lambda une, b: bool(une ou b), [[[[0, 0, 1, 1, 0])
Vrai
>>> reduce(lambda une, b: bool(une ou b), [[[[0, 0, 0, 0, 0])
Faux
>>> reduce(lambda une, b: bool(une ou b), [], Faux)
Faux
Ce lambda
function is quite similar to any_true()
. It returns Vrai
if either of its two arguments is true. If both arguments are false, then it returns Faux
.
Even though this solution takes only one line of code, it can still make your code unreadable or at least difficult to understand. Again, Python provides a tool to efficiently solve the any-true problem without using reduce()
: the built-in function any()
.
any(iterable)
loops over the items in iterable
, testing the truth value of each until it finds a true item. The function returns Vrai
as soon as it finds a true value. Si any()
doesn’t find a true value, then it returns Faux
. Voici un exemple:
>>> tout([[[[0, 0, 0, 0, 0])
Faux
>>> tout([[[[0, 0, 0, 1, 0])
Vrai
>>> tout([])
Faux
Again, you don’t need to import any()
to use it in your code. any()
works as expected. It returns Faux
if all the items in the iterable are false. Otherwise, it returns Vrai
. Note that if you call any()
with an empty iterable, then you get Faux
because there’s no true item in an empty iterable.
Comme avec all()
, any()
is a C function optimized for performance. It’s also implemented using short-circuit evaluation. So, if you’re dealing with the any-true problem in Python, then consider using any()
instead of reduce()
.
Comparant reduce()
et accumulate()
A Python function called accumulate()
lives in itertools
and behaves similarly to reduce()
. accumulate(iterable[, func])
accepts one required argument, iterable
, which can be any Python iterable. The optional second argument, func
, needs to be a function (or a callable object) that takes two arguments and returns a single value.
accumulate()
returns an iterator. Each item in this iterator will be the accumulated result of the computation that func
performs. The default computation is the sum. If you don’t supply a function to accumulate()
, then each item in the resulting iterator will be the accumulated sum of the previous items in iterable
plus the item at hand.
Check out the following examples:
>>> de itertools import accumulate
>>> de operator import ajouter
>>> de functools import reduce
>>> numbers = [[[[1, 2, 3, 4]
>>> liste(accumulate(numbers))
[1, 3, 6, 10]
>>> reduce(ajouter, numbers)
dix
Note that the last value in the resulting iterator is the same value that reduce()
returns. This is the main similarity between these two functions.
Remarque: Puisque accumulate()
returns an iterator, you need to call list()
to consume the iterator and get a list object as an output.
If, on the other hand, you supply a two-argument function (or callable) to the func
argument of accumulate()
, then the items in the resulting iterator will be the accumulated result of the computation performed by func
. Here’s an example that uses operator.mul()
:
>>> de itertools import accumulate
>>> de operator import mul
>>> de functools import reduce
>>> numbers = [[[[1, 2, 3, 4]
>>> liste(accumulate(numbers, mul))
[1, 2, 6, 24]
>>> reduce(mul, numbers)
24
In this example, you can again see that the last item in the returned value of accumulate()
is equal to the value returned by reduce()
.
Considering Performance and Readability
Python’s reduce()
can have remarkably bad performance because it works by calling functions multiple times. This can make your code slow and inefficient. En utilisant reduce()
can also compromise the readability of your code when you use it with complex user-defined functions or lambda
les fonctions.
Throughout this tutorial, you’ve learned that Python offers a bunch of tools that can gracefully replace reduce()
, at least for its main use cases. Here are the main takeaways of your reading up to this point:
-
Use a dedicated function to solve use cases for Python’s
reduce()
whenever possible. Functions such assum()
,all()
,any()
,max()
,min()
,len()
,math.prod()
, and so on will make your code faster and more readable, maintainable, and Pythonic. -
Avoid complex user-defined functions when using
reduce()
. These kinds of functions can make your code difficult to read and understand. You can use an explicit and readablepour
loop instead. -
Avoid complex
lambda
les fonctions when usingreduce()
. They can also make your code unreadable and confusing.
The second and third points were concerns for Guido himself when he said the following:
So now
reduce()
. This is actually the one I’ve always hated most, because, apart from a few examples involving+
ou*
, almost every time I see areduce()
call with a non-trivial function argument, I need to grab pen and paper to diagram what’s actually being fed into that function before I understand what thereduce()
is supposed to do. So in my mind, the applicability ofreduce()
is pretty much limited to associative operators, and in all other cases it’s better to write out the accumulation loop explicitly. (La source)
The next two sections will help you implement this general advice in your code. They also provide some extra advice that will help you use Python’s reduce()
effectively when you really need to use it.
Performance Is Key
If you’re going to use reduce()
to solve the use cases that you’ve covered in this tutorial, then your code will be considerably slower as compared to code using dedicated built-in functions. In the following examples, you’ll use timeit.timeit()
to quickly measure the execution time of small bits of Python code and get an idea of their general performance.
timeit()
takes several arguments, but for these examples, you’ll only need to use the following:
stmt
holds the statement that you need to time.setup
takes additional statements for general setup, likeimport
statements.globals
holds a dictionary containing the global namespace that you need to use for runningstmt
.
Take a look at the following examples that time the sum use case en utilisant reduce()
with different tools and using Python’s sum()
for comparison purposes:
>>> de functools import reduce
>>> de timeit import timeit
>>> # Using a user-defined function
>>> def ajouter(une, b):
... revenir une + b
...
>>> use_add = "functools.reduce(add, range(100))"
>>> timeit(use_add, "import functools", globals="add": ajouter)
13.443158069014316
>>> # Using a lambda expression
>>> use_lambda = "functools.reduce(lambda x, y: x + y, range(100))"
>>> timeit(use_lambda, "import functools")
11.998800784000196
>>> # Using operator.add()
>>> use_operator_add = "functools.reduce(operator.add, range(100))"
>>> timeit(use_operator_add, "import functools, operator")
5.183870767941698
>>> # Using sum()
>>> timeit("sum(range(100))", globals="sum": sum)
1.1643308430211619
Even though you’ll get different numbers depending on your hardware, you’ll likely get the best time measurement using sum()
. This built-in function is also the most readable and Pythonic solution for the sum problem.
Your second-best option would be to use reduce()
avec operator.add()
. The functions in operator
are written in C and are highly optimized for performance. So, they should perform better than a user-defined function, a lambda
function, or a pour
loop.
Readability Counts
Code readability is also an important concern when it comes to using Python’s reduce()
. Even though reduce()
will generally perform better than a Python pour
loop, as Guido himself stated, a clean and Pythonic loop is often easier to follow than using reduce()
.
The What’s New In Python 3.0 guide reinforces this idea when it says the following:
Utilisation
functools.reduce()
if you really need it; however, 99 percent of the time an explicitpour
loop is more readable. (La source)
To better understand the importance of readability, imagine that you’re starting to learn Python and you’re trying to solve an exercise about calculating the sum of all the even numbers in an iterable.
If you already know about Python’s reduce()
and have done some functional programming in the past, then you might come up with the following solution:
>>> de functools import reduce
>>> def sum_even(it):
... revenir reduce(lambda X, y: X + y si ne pas y % 2 autre X, it, 0)
...
>>> sum_even([[[[1, 2, 3, 4])
6
In this function, you use reduce()
to cumulatively sum the even numbers in an iterable. le lambda
function takes two arguments, X
et y
, and returns their sum if they’re even. Otherwise, it returns X
, which holds the result of the previous sum.
Additionally, you set initializer
à 0
because otherwise your sum will have an initial value of 1
(the first value in iterable
), which isn’t an even number and will introduce a bug into your function.
The function works as you expected, and you’re happy with the result. However, you continue digging into Python and learn about sum()
and generator expressions. You decide to rework your function using these new tools, and your function now looks as follows:
>>> def sum_even(iterable):
... revenir sum(num pour num dans iterable si ne pas num % 2)
...
>>> sum_even([[[[1, 2, 3, 4])
6
When you look at this code, you feel really proud, and you should. You’ve done a great job! That’s a beautiful Python function that almost reads as plain English. It’s also efficient and Pythonic. Qu'est-ce que tu penses?
Conclusion
Python’s reduce()
allows you to perform reduction operations on iterables using Python callables and lambda
les fonctions. reduce()
applies a function to the items in an iterable and reduces them to a single cumulative value.
In this tutorial, you’ve learned:
- Quoi réduction, ou folding, is and when it might be useful
- How to use Python’s
reduce()
to solve common reduction problems like summing or multiplying numbers - Lequel Pythonic tools you can use to effectively replace
reduce()
in your code
With this knowledge, you’ll be able to decide which tools best fit your coding needs when it comes to solving reduction problems in Python.
Over the years, reduce()
has been replaced by more Pythonic tools like sum()
, min()
, max()
all()
, any()
, entre autres. cependant, reduce()
is still there and is still popular among functional programmers. If you have questions or thoughts about using reduce()
or any of its Python alternatives, then be sure to post them in the comments below.
[ad_2]