Du style fonctionnel au style pythonique – Real Python

By | juin 29, 2020

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() et carte(), 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:

  1. Appliquer une fonction (ou appelable) aux deux premiers éléments dans un itérable et générer un résultat partiel.
  2. Utilisation ce résultat partiel, avec le troisième élément de l'itérable, pour générer un autre résultat partiel.
  3. 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:

  1. import functools puis utilisez des noms complets comme functools.reduce ().
  2. de functools import réduire puis appeler ré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.

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.

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 bet 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.

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:

  1. Une fonction définie par l'utilisateur
  2. UNE lambda fonction
  3. 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].

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 bet 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.

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 et Faux
  • Types numériques avec une valeur nulle comme 0, 0,0, 0j, Décimal (0), et Fraction (0, 1)
  • Séquences vides et collections comme "", (), [], , ensemble(), et plage (0)
  • Objets qui implémentent __bool __ () avec une valeur de retour de Faux ou __len __ () avec une valeur de retour de 0

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.

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.

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:

  1. Use a dedicated function to solve use cases for Python’s reduce() whenever possible. Functions such as sum(), all(), any(), max(), min(), len(), math.prod(), and so on will make your code faster and more readable, maintainable, and Pythonic.

  2. 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 readable pour loop instead.

  3. Avoid complex lambda les fonctions when using reduce(). 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 a reduce() 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 the reduce() is supposed to do. So in my mind, the applicability of reduce() 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, like import statements.
  • globals holds a dictionary containing the global namespace that you need to use for running stmt.

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 explicit pour 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.