La fonction Python « reduce() » est issue du domaine de la programmation fonctionnelle. La programmation fonctionnelle (FP) est un paradigme de programmation dans lequel les programmes produisent des résultats en appliquant des fonctions à des données immuables.
Un modèle courant dans ce style est le « pli », qui réduit une séquence en un seul résultat. Par exemple, plier la liste des nombres [2, 4, 5, 3] sous l'addition 14 par étapes successives : [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce() généralise cette idée. Il applique une opération binaire à un itérable jusqu'à ce que seul le résultat subsiste.
Dans cet article, j'examinerai les éléments clés de l'reduce() de Python et donnerai quelques exemples pratiques. Si vous avez besoin d'une remise à niveau sur les bases de Python, je vous recommande de consulter les ressources suivantes :
Python inclut d'autres fonctions de programmation fonctionnelle, telles que map() et filter().
Les constructions fonctionnelles telles que map() et filter() étaient présentes dans Python 1.0. Guido van Rossum n'appréciait pas ces constructions, soulignant que l'instruction « reduce() » était difficile à analyser et qu'une boucle « for » était presque toujours plus lisible. Dans Python 3.0, conformément à la PEP 3100, les développeurs ont supprimé la fonction intégrée ` reduce() ` et l'ont déplacée vers le module ` functools `. Le passage à functools a essentiellement relégué cet outil au statut d'outil de niche.
Pourquoi utiliser reduce() ?
La plupart du temps, je considère qu'il est probablement préférable d'utiliser une fonction intégrée ou une boucle. Cependant, reduce() reste un choix judicieux dans certains cas d'utilisation.
Pipelines de fonctions. Enchaînez une série (éventuellement dynamique) de transformations de manière claire.
Pliages algébriques. À utiliser pour les opérations qui ont des valeurs d'identité naturelles, telles que l'union d'ensembles avec l'ensemble vide ou les opérations de masque de bits avec zéro.
Pli personnalisé sans intégration. Définissez votre propre fusion de manière spécifique au domaine lorsqu'il n'existe aucune fusion intégrée.
Accumulateurs structurés. Menez simultanément plusieurs éléments d'état dans une fonction d'accumulateur personnalisée.
Fonctionnement de la fonction reduce() en Python
Veuillez examiner le fonctionnement de reduce().
La signature de la fonction de base d'reduce() est la suivante :
functools.reduce(function, iterable,[initializer])Propulsé par
La fonction reduce() fonction requiert deux arguments obligatoires et un troisième facultatif.
function: une fonction binaire qui définit comment combiner deux éléments,
iterable: la séquence ou l'itérable à réduire, tel qu'une liste ou un tuple, initializer(facultatif) : une valeur de départ pour initialiser la fonction.
Exemple étape par étape :
[1, 3, 2, 7] → [4, 2, 7].
[4, 2, 7] → [6, 7].
[6, 7] → [13].
Le résultat final est 13.
Exemples simples de reduce()
Les exemples suivants illustrent le fonctionnement de l'utilisation de reduce().
from functools importreduce
numbers =[2,4,6]
product =reduce(lambda x, y: x * y, numbers)# ((2 x 4) x 6) = 8 x 6 = 48
min_value =reduce(lambda x, y: x if x < y else y, numbers)# 2
words =['dog','cat','tree','pony']
str_concat =reduce(lambda x, y: x + y, words)# "dogcattreepony"Propulsé par
Initialiseur
Sans initialiseur, la fonction ` reduce() ` prend le premier élément de l'itérable comme valeur de départ. Si l'itérable est vide, l'opération d'reduce() ion renvoie une exception d'TypeError. Afin de garantir la robustesse du code, veuillez fournir un initialiseur pour définir le comportement en cas d'entrée vide.
from functools importreduce
words =[]# Error: reduce() of empty iterable with no initial value
str_concat =reduce(lambda x, y: x + y, words)# Correct: use empty string initializer
str_concat =reduce(lambda x, y: x + y, words,"")Propulsé par
Les initialiseurs fournissent également un résultat avec un conteneur vide. Par exemple, vous pouvez concaténer des mots dans une liste plate de caractères.
from functools importreduce
words =['reduce','is','fun']
chars_list =reduce(lambda acc, word: acc +list(word), words,[])print(chars_list)# ['r', 'e', 'd', 'u', 'c', 'e', 'i', 's', 'f', 'u', 'n']Propulsé par
Pour approfondir vos connaissances en Python et en manipulation de données, voici quelques options que je recommande.
Jusqu'à présent, nous avons utilisé des fonctions lambda pour définir l'opérateur binaire.
Vous pouvez également utiliser les opérateurs du module operator. Le moduleoperator contient des versions fonctionnelles d'opérateurs courants et d'appels de méthodes. Par exemple, au lieu de x + y, operator.add(x, y). Cela vous permet de transmettre des opérateurs prédéfinis (et efficaces) à l'reduce() sans avoir à écrire de lambda.
from functools importreduceimport operator as op
numbers =[2,4,6]
total =reduce(op.add, numbers,0)# instead of reduce(lambda x,y: x + y, numbers)Propulsé par
Une troisième option consiste à créer une fonction personnalisée. Il s'agit d'une option appropriée lorsqu'il n'existe pas d'opérateur prédéfini ou lorsque la fonction est trop complexe pour un lambda.
Par exemple, supposons que vous souhaitiez supprimer les doublons d'une liste, mais en conservant l'ordre de leur première apparition. Vous pourriez définir un réducteur qui ajoute un élément à une liste uniquement s'il n'y figure pas déjà.
from functools importreduce
items =['the','wild','wild','world','is','the','wide','world','is','the','world']defdedup(acc, x):if x notin acc:# O(n) membership test
acc.append(x)return acc
unique =reduce(dedup, items,[])# ['the', 'wild', 'world', 'is', 'wide']Propulsé par
Pour plus d'informations sur la suppression des doublons, veuillez consulter ce tutoriel.
Python reduce() appelle notre fonction une fois pour chaque élément. Dans une liste contenant un million d'éléments, cela implique un million d'appels de fonction, chacun créant une trame, traitant des arguments et mettant à jour les comptes de référence. Cela entraîne des frais supplémentaires importants.
En revanche, une fonction intégrée telle que sum effectue un seul appel à une fonction C et exécute les millions d'additions à l'intérieur de la boucle C. Cette différence peut accélérer considérablement les commandes intégrées.
Problèmes liés à la localisation du cache et à l'efficacité du processeur
La réduction est également affectée par la localité du cache et l'efficacité du processeur.
Un processeur moderne peut exécuter plusieurs milliards d'instructions par seconde, mais l'accès à la mémoire vive est considérablement plus lent. Pour compenser, les processeurs modernes sont équipés decaches d's (L1, L2, L3) qui stockent les données pour un accès rapide.
Ces caches exploitent deux modèles.
Localité temporelle Les données récemment utilisées sont susceptibles d'être réutilisées.
La localisation spatiale Les données proches d'une adresse récemment utilisée sont susceptibles d'être utilisées prochainement.
En revanche, chaque étape de l'reduce() e implique la recherche du pointeur pour trouver l'élément suivant et des appels de fonctions Python, ce qui perturbe la localité et ralentit le CPU. Les fonctions intégrées et vectorisées permettent d'éviter ce problème en exécutant des boucles C serrées.
Pour rafraîchir vos connaissances sur la rédaction de code Python efficace et idiomatique, veuillez consulter :
Code C optimisé. Les fonctions intégrées exécutent leurs boucles dans un code C optimisé, et non en Python. Cela permet d'éviter les frais généraux liés à l'reduce(). Cet avantage en termes de vitesse est encore plus marqué pour les entrées volumineuses.
Lisibilité. Les fonctions intégrées ont des noms descriptifs (sum, min) afin que leur objectif soit clair. Un appel à reduce() vous permet d'analyser la fonction en cours de pliage.
Boucles :
Performance. Les boucles s'exécutent généralement plus lentement que les fonctions intégrées, mais plus rapidement que l'reduce().
Lisibilité. Tout comme les fonctions intégrées, les boucles sont généralement plus lisibles que l'reduce(). Un appel à reduce() vous oblige à analyser une instruction fonctionnelle, alors qu'une boucle est plus conforme au style Python.
itertools.accumulate()
La bibliothèque Python « itertools » fournit un ensemble d'itérateurs performants tels que « count() », « product() » et « combinations() ». Une fonction utile d'itertools s est itertools.accumulate(). À l'instar de reduce(), il applique une fonction à un itérable. Cependant, accumulate() stocke les valeurs intermédiaires du calcul, et pas seulement le résultat final.
Par exemple,
import itertools, operator
from functools importreducelist(itertools.accumulate([1,2,3,4], initial=0))# [0, 1, 3, 6, 10]reduce(operator.add,[1,2,3,4],0)# 10Propulsé par
La fonction Accumuler est utile lorsque vous avez besoin de totaux cumulés ou de minimums/maximums. Par exemple, vous pourriez souhaiter connaître la température maximale mois après mois.
Erreurs courantes lors de l'utilisation de reduce()
Lorsque vous utilisez reduce(), veuillez garder à l'esprit les points suivants.
Veuillez privilégier des alternatives plus simples telles que les intégrations ou les boucles. Veuillez conserver reduce() pour une utilisation ultérieure.
Gérer les itérables vides. Veuillez toujours utiliser un initialiseur approprié afin d'éviter toute erreur en cas de saisie vide.
Veuillez prêter attention aux problèmes de mémoire. Veuillez ne pas appliquer de force l'reduce() dans des situations où une approche par générateur ou streaming serait plus efficace.
Veuillez éviter les lambdas complexes. Veuillez utiliser les fonctions du module operator lorsque cela est possible. Les lambdas, en particulier avec des opérations non associatives, peuvent nuire à la clarté.
Privilégiez la clarté à l'ingéniosité.
Meilleures pratiques et directives pour la fonction reduce() en Python
Comme pour tout outil, il existe des pratiques exemplaires à respecter avec reduce(). Si vous avez déterminé que reduce() est l'outil approprié à utiliser, voici quelques directives pour son utilisation.
Veuillez concevoir le réducteur en premier lieu.
Veuillez définir le contrat en une seule phrase. « Regroupez les clés du dictionnaire en additionnant les comptes par clé. »
Veuillez conserver le caractère associatif si possible. Cela vous permet de paralléliser et de tester plus facilement.
Veuillez identifier votre élément d'identité et l'utiliser comme initialiseur. Par exemple, pour la somme, l'identité est 0. Pour min, l'adresse est math.inf, et pour set union, l'adresse est set(). TypeErrorsCela permet de garantir la robustesse du code et de le préserver des erreurs de programmation.
Veuillez privilégier la simplicité et la lisibilité.
Une opération claire. Aucun effet secondaire.
Veuillez attribuer un nom descriptif au réducteur.
Document
Dans la chaîne de documentation, veuillez noter l'élément d'identité et le comportement en cas d'entrée vide, l'hypothèse d'associativité et la politique en matière d'erreurs.
Test
Tests unitaires sur les cas limites : itérable vide, types mixtes, valeurs extrêmes.
Teste l'associativité.
Surveiller les performances
Veuillez comparer les petites et grandes entrées. Veuillez comparer les benchmarks aux fonctions intégrées et aux boucles.
Si la vitesse est un facteur important, envisagez le prétraitement, le traitement par lots et le transfert des calculs mathématiques lourds vers NumPy ou pandas.
Applications avancées et cas d'utilisation concrets de la fonction Python reduce()
Compte tenu de ces inconvénients, on pourrait penser que l'reduce() e n'a aucune valeur réelle. Au contraire, reduce() présente de nombreux cas d'utilisation utiles.
Traitement des structures imbriquées
Opérations de type base de données
Pipelines de traitement des données
Applications MapReduce
Traitement des structures imbriquées
Reduce offre un moyen efficace de parcourir des structures de données imbriquées, telles que les objets JSON, en regroupant une séquence de clés en recherches successives.
import json
from functools importreduceimport operator
data = json.loads('''
{
"user": {
"id": "ABC123",
"name": "Alice",
"email": "[email protected]",
"profile": {
"address": {
"city": "San Francisco",
"zip": "94103"
},
"age": 34,
"skills": ["Python", "Data Science", "Machine Learning"]
}
}
}
''')# Example lookups with reduce + operator.getitem
city =reduce(operator.getitem,["user","profile","address","city"], data)print(city)# "San Francisco"
user_id =reduce(operator.getitem,["user","id"], data)print(user_id)# "ABC123"
age =reduce(operator.getitem,["user","profile","age"], data)print(age)# 34Propulsé par
Il est judicieux d'utiliser reduce() dans ce contexte. Le JSON est profondément imbriqué : user → profile → address → city. Au lieu d'enchaîner manuellement les recherches, veuillez représenter le chemin sous forme de liste de clés. Veuillez ensuite utiliser reduce(operator.getitem, path, data) pour le parcourir. Cela permet de conserver un code générique, lisible et réutilisable.
Pipelines de traitement des données
Reduce peut piloter des pipelines de traitement de données en faisant passer les données par une séquence de transformations. Chaque fonction traite une seule étape, et le pipeline résulte de leur application dans l'ordre. Voici un pipeline de démonstration qui prétraite une chaîne de texte avant de l'introduire dans un modèle NLP.
from functools importreduceimport re
# Define preprocessing stepsdefstrip_punctuation(s):return re.sub(r"[^\w\s]","", s)defto_lower(s):return s.lower()defremove_stopwords(s):
stops ={"the","is","a","of"}return" ".join(word for word in s.split()if word notin stops)defstem_words(s):# trivial "stemmer": cut off 'ing'return" ".join(word[:-3]if word.endswith("ing")else word for word in s.split())
pipeline =[
strip_punctuation,
to_lower,
remove_stopwords,
stem_words,]# Input data
text ="The quick brown fox is Jumping over a log."# Apply pipeline with reduce
processed =reduce(lambda acc, f: f(acc), pipeline, text)print(processed)# quick brown fox jump over logPropulsé par
Gestion des erreurs dans les applications complexes
Revenons à l'exemple JSON imbriqué. À l'heure actuelle, l'appel direct à reduce(operator.getitem, …) renvoie une exception KeyError ou TypeError si une clé est manquante ou s'il rencontre un élément qui n'est pas un dictionnaire. Pour renforcer la sécurité du code, veuillez définir une fonction d'aide qui encapsule operator.getitem dans un bloc try/except et renvoie une valeur par défaut en cas d'erreur.
Voici une possibilité pour la fonction d'aide.
defdeep_get(data, keys, default=None):"""Traverse nested dicts/lists safely with reduce."""try:returnreduce(operator.getitem, keys, data)except(KeyError, IndexError, TypeError):return defaultPropulsé par
Maintenant, modifiez les exemples de recherche pour utiliser notre nouvelle fonction au lieu d'une fonction déballée. reduce()
# Example lookups
city = deep_get(data,["user","profile","address","city"], default="Unknown City")print(city)# "San Francisco"
user_id = deep_get(data,["user","id"], default="N/A")print(user_id)# "ABC123"
age = deep_get(data,["user","profile","age"], default="N/A")print(age)# 34# Example with missing key
phone = deep_get(data,["user","profile","phone"], default="No phone")print(phone)# "No phone"Propulsé par
Transformations de données en plusieurs étapes avec map() et filter()
Vous pouvez associer reduce() à d'autres outils fonctionnels, tels que map() et filter(), afin de créer des transformations de données en plusieurs étapes. Voici notre pipeline de prétraitement NLP précédent, rédigé de manière fonctionnelle.
from functools importreduceimport re
# Define preprocessing stepsdefstrip_punctuation(s):return re.sub(r"[^\w\s]","", s)defto_lower(s):return" ".join(map(str.lower, s.split()))defremove_stopwords(s):
stops ={"the","is","a","of"}return" ".join(filter(lambda w: w notin stops, s.split()))defstem_words(s):# trivial "stemmer": cut off 'ing'return" ".join(map(lambda w: w[:-3]if w.endswith("ing")else w, s.split()))# Pipeline of transformations
pipeline =[
strip_punctuation,
to_lower,
remove_stopwords,
stem_words,]# Input data
text ="The quick brown fox is Jumping over a log."# Apply pipeline with reduce
processed =reduce(lambda acc, f: f(acc), pipeline, text)print(processed)# quick brown fox jump over logPropulsé par
Les transformations sont les suivantes :
Supprimer la ponctuation. « Le renard brun rapide saute par-dessus une bûche. » → « Le renard brun rapide saute par-dessus une bûche. »
Minuscules
« Le renard brun rapide saute par-dessus une bûche » → « le renard brun rapide saute par-dessus une bûche »
Supprimer les mots vides. « Le renard brun rapide saute par-dessus une bûche » → « Renard brun rapide sautant par-dessus une bûche »
Mots racines
« rapide renard brun sautant sur une bûche » → « rapide renard brun saute sur une bûche »
Pour approfondir vos connaissances en matière de programmation fonctionnelle et de vectorisation, nous vous recommandons ces articles DataCamp.
Pour utiliser efficacement l'reduce(), il est utile de comprendre comment il s'intègre dans l'écosystème Python moderne. Explorons comment il s'intègre à NumPy et pandas, comment il soutient les systèmes parallèles et distribués, et comment il interagit avec les outils modernes tels que les analyseurs statiques.
numpy et pandas
NumPy et pandas fonctionnent à partir d'un code C optimisé, il est donc déconseillé de reproduire leurs fonctionnalités avec reduce(). Cependant, reduce() constitue un choix judicieux pour les pipelines comportant des étapes dynamiques. Par exemple, vous pourriez composer de nombreuses transformations NumPy sur un tableau.
from functools importreduceimport numpy as np
defstandardize(x):return(x - x.mean())/(x.std()+1e-9)defclip(x):return np.clip(x,0,1)deflog(x):return np.log1p(x)
x = np.array([1,500,40.5,100,250.45])
funcs =[standardize, clip, log]
y =reduce(lambda a, f: f(a), funcs, x)# x is ndarrayPropulsé par
Cadres informatiques parallèles et distribués
La réduction est un élément central des cadres informatiques parallèles et distribués. Ces systèmes fonctionnent en divisant un ensemble de données en partitions, en traitant ces partitions en parallèle, puis en combinant ces résultats partiels en une seule réponse. L'étape de « combinaison » correspond à la réduction.
Pour que la réduction fonctionne correctement dans un cluster, veuillez vous assurer que les conditions suivantes sont remplies.
Associativité. L'opération doit donner le même résultat quel que soit le regroupement. Cela permet de fusionner les résultats partiels dans n'importe quel ordre à travers le réseau.
Élément identitaire. L'opération doit comporter un initialiseur qui n'affecte pas le résultat final. Cela garantit l'exactitude lorsque les partitions sont vides ou de taille inégale.
Si ces propriétés ne sont pas respectées, les réductions deviennent lentes (car elles ne peuvent pas être parallélisées en toute sécurité) ou incorrectes (car les résultats dépendent de l'ordre d'évaluation).
Ce processus nécessite une opération associative et une identité claire. Dans le cas contraire, vous obtiendrez un code lent ou des résultats incorrects.
Outils d'analyse statique
Un analyseur statique (tel que mypy, Pyright, ruff ou bandit) est un outil qui inspecte le code sans l'exécuter. Ces outils détectent les erreurs, appliquent les règles de style et vérifient l'exactitude des types.
Les analyseurs statiques rencontrent des difficultés avec l'reduce(). Dans le processus de pliage, le type d'accumulateur peut différer du type d'élément, et l'inférence de type devient confuse.
Veuillez examiner ce code.
from functools importreducedefadd_chars(acc, word):
acc.extend(word)# extend adds each character of the stringreturn acc
chars =reduce(add_chars,["hi","ok"],[])print(chars)# ['h', 'i', 'o', 'k']Propulsé par
Même si ce code fonctionne correctement, un analyseur statique pourrait signaler quelques problèmes.
Le type d'élément de l'initialiseur [] est ambigu.
Les analyseurs peuvent supposer que les types d'accumulateur et d'élément correspondent.
Afin de rendre le code plus clair pour les humains et les analyseurs, veuillez ajouter des indications de type.
from functools importreducefrom typing import List
defadd_chars(acc: List[str], word:str)-> List[str]:
acc.extend(word)# extend adds each character of the stringreturn acc
chars: List[str]=reduce(add_chars,["hi","ok"],[])print(chars)# ['h', 'i', 'o', 'k']Propulsé par
Le réducteur affiche désormais explicitement
L'accumulateur est un dispositif de stockage d'énergie ( List[str]).
Chaque élément est un str.
Le type de retour est un objet de type « list[str] ».
Grâce à ces indications, les analyseurs statiques peuvent vérifier le code de manière rigoureuse.
Conclusion
La fonction « reduce » provient de la programmation fonctionnelle, où la consolidation de collections en un seul résultat constitue un concept central. Même s'il ne s'agit plus d'un outil de premier ordre en Python, il a toujours sa place. Lorsque vous avez besoin de pipelines flexibles, de pliages personnalisés ou d'opérations qui ne correspondent pas parfaitement aux fonctions existantes, reduce() est un outil puissant. Utilisé avec précaution, il s'intègre parfaitement à l'écosystème Python au sens large et reste un outil pratique dans les situations appropriées.
Voici quelques liens Python connexes qui pourraient vous être utiles.
Découvrez les fonctions lambda Python, leur utilité et quand les utiliser. Comprend des exemples pratiques et des bonnes pratiques pour une mise en œuvre efficace.
Découvrez les fonctions et méthodes des listes Python. Veuillez suivre les exemples de code pour list() et d'autres fonctions et méthodes Python dès maintenant.
Apprenez à diviser des listes Python à l'aide de techniques telles que le découpage, les compréhensions de listes et itertools. Veuillez découvrir quand utiliser chaque méthode pour une gestion optimale des données.
Développez vos compétences en matière de données, découvrez comment manipuler et visualiser les données, et appliquez des analyses avancées pour prendre des décisions basées sur les données.
La fonction Python «
reduce()» est issue du domaine de la programmation fonctionnelle. La programmation fonctionnelle (FP) est un paradigme de programmation dans lequel les programmes produisent des résultats en appliquant des fonctions à des données immuables.Un modèle courant dans ce style est le « pli », qui réduit une séquence en un seul résultat. Par exemple, plier la liste des nombres [2, 4, 5, 3] sous l'addition 14 par étapes successives : [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce()généralise cette idée. Il applique une opération binaire à un itérable jusqu'à ce que seul le résultat subsiste.Dans cet article, j'examinerai les éléments clés de l'
reduce()de Python et donnerai quelques exemples pratiques. Si vous avez besoin d'une remise à niveau sur les bases de Python, je vous recommande de consulter les ressources suivantes :Histoire
Python inclut d'autres fonctions de programmation fonctionnelle, telles que
map()etfilter().Les constructions fonctionnelles telles que
map()etfilter()étaient présentes dans Python 1.0. Guido van Rossum n'appréciait pas ces constructions, soulignant que l'instruction «reduce()» était difficile à analyser et qu'une boucle «for» était presque toujours plus lisible. Dans Python 3.0, conformément à la PEP 3100, les développeurs ont supprimé la fonction intégrée `reduce()` et l'ont déplacée vers le module `functools`. Le passage àfunctoolsa essentiellement relégué cet outil au statut d'outil de niche.Pourquoi utiliser reduce() ?
La plupart du temps, je considère qu'il est probablement préférable d'utiliser une fonction intégrée ou une boucle. Cependant,
reduce()reste un choix judicieux dans certains cas d'utilisation.Fonctionnement de la fonction reduce() en Python
Veuillez examiner le fonctionnement de
reduce().La signature de la fonction de base d'
reduce()est la suivante :La fonction
reduce()fonction requiert deux arguments obligatoires et un troisième facultatif.function: une fonction binaire qui définit comment combiner deux éléments,iterable: la séquence ou l'itérable à réduire, tel qu'une liste ou un tuple,initializer(facultatif) : une valeur de départ pour initialiser la fonction.Exemple étape par étape :
Le résultat final est 13.
Exemples simples de reduce()
Les exemples suivants illustrent le fonctionnement de l'utilisation de
reduce().Initialiseur
Sans initialiseur, la fonction `
reduce()` prend le premier élément de l'itérable comme valeur de départ. Si l'itérable est vide, l'opération d'reduce()ion renvoie une exception d'TypeError. Afin de garantir la robustesse du code, veuillez fournir un initialiseur pour définir le comportement en cas d'entrée vide.Les initialiseurs fournissent également un résultat avec un conteneur vide. Par exemple, vous pouvez concaténer des mots dans une liste plate de caractères.
Pour approfondir vos connaissances en Python et en manipulation de données, voici quelques options que je recommande.
Définition du réducteur
Jusqu'à présent, nous avons utilisé des fonctions lambda pour définir l'opérateur binaire.
Vous pouvez également utiliser les opérateurs du module
operator. Le moduleoperatorcontient des versions fonctionnelles d'opérateurs courants et d'appels de méthodes. Par exemple, au lieu de x + y,operator.add(x, y). Cela vous permet de transmettre des opérateurs prédéfinis (et efficaces) à l'reduce() sans avoir à écrire de lambda.Une troisième option consiste à créer une fonction personnalisée. Il s'agit d'une option appropriée lorsqu'il n'existe pas d'opérateur prédéfini ou lorsque la fonction est trop complexe pour un lambda.
Par exemple, supposons que vous souhaitiez supprimer les doublons d'une liste, mais en conservant l'ordre de leur première apparition. Vous pourriez définir un réducteur qui ajoute un élément à une liste uniquement s'il n'y figure pas déjà.
Pour plus d'informations sur la suppression des doublons, veuillez consulter ce tutoriel.
Performances de la fonction reduce() en Python
reduce()implique des problèmes de performance.Surcoût lié à l'appel de fonction
Python
reduce()appelle notre fonction une fois pour chaque élément. Dans une liste contenant un million d'éléments, cela implique un million d'appels de fonction, chacun créant une trame, traitant des arguments et mettant à jour les comptes de référence. Cela entraîne des frais supplémentaires importants.En revanche, une fonction intégrée telle que
sumeffectue un seul appel à une fonction C et exécute les millions d'additions à l'intérieur de la boucle C. Cette différence peut accélérer considérablement les commandes intégrées.Problèmes liés à la localisation du cache et à l'efficacité du processeur
La réduction est également affectée par la localité du cache et l'efficacité du processeur.
Un processeur moderne peut exécuter plusieurs milliards d'instructions par seconde, mais l'accès à la mémoire vive est considérablement plus lent. Pour compenser, les processeurs modernes sont équipés decaches d' s (L1, L2, L3) qui stockent les données pour un accès rapide.
Ces caches exploitent deux modèles.
En revanche, chaque étape de l'
reduce()e implique la recherche du pointeur pour trouver l'élément suivant et des appels de fonctions Python, ce qui perturbe la localité et ralentit le CPU. Les fonctions intégrées et vectorisées permettent d'éviter ce problème en exécutant des boucles C serrées.Pour rafraîchir vos connaissances sur la rédaction de code Python efficace et idiomatique, veuillez consulter :
Alternatives à la fonction Python reduce()
Éléments encastrés:
reduce(). Cet avantage en termes de vitesse est encore plus marqué pour les entrées volumineuses.sum,min) afin que leur objectif soit clair. Un appel àreduce()vous permet d'analyser la fonction en cours de pliage.Boucles :
reduce().reduce(). Un appel àreduce()vous oblige à analyser une instruction fonctionnelle, alors qu'une boucle est plus conforme au style Python.itertools.accumulate()La bibliothèque Python «
itertools» fournit un ensemble d'itérateurs performants tels que «count()», «product()» et «combinations()». Une fonction utile d'itertoolss estitertools.accumulate(). À l'instar dereduce(), il applique une fonction à un itérable. Cependant,accumulate() stocke les valeurs intermédiaires du calcul, et pas seulement le résultat final.Par exemple,
La fonction Accumuler est utile lorsque vous avez besoin de totaux cumulés ou de minimums/maximums. Par exemple, vous pourriez souhaiter connaître la température maximale mois après mois.
Erreurs courantes lors de l'utilisation de reduce()
Lorsque vous utilisez
reduce(), veuillez garder à l'esprit les points suivants.reduce()pour une utilisation ultérieure.reduce()dans des situations où une approche par générateur ou streaming serait plus efficace.operatorlorsque cela est possible. Les lambdas, en particulier avec des opérations non associatives, peuvent nuire à la clarté.Meilleures pratiques et directives pour la fonction reduce() en Python
Comme pour tout outil, il existe des pratiques exemplaires à respecter avec
reduce(). Si vous avez déterminé quereduce()est l'outil approprié à utiliser, voici quelques directives pour son utilisation.Veuillez concevoir le réducteur en premier lieu.
min, l'adresse estmath.inf, et pourset union, l'adresse estset().TypeErrorsCela permet de garantir la robustesse du code et de le préserver des erreurs de programmation.Veuillez privilégier la simplicité et la lisibilité.
Document
Test
Surveiller les performances
Applications avancées et cas d'utilisation concrets de la fonction Python reduce()
Compte tenu de ces inconvénients, on pourrait penser que l'
reduce()e n'a aucune valeur réelle. Au contraire,reduce()présente de nombreux cas d'utilisation utiles.Traitement des structures imbriquées
Reduce offre un moyen efficace de parcourir des structures de données imbriquées, telles que les objets JSON, en regroupant une séquence de clés en recherches successives.
Il est judicieux d'utiliser
reduce()dans ce contexte. Le JSON est profondément imbriqué :user → profile → address → city. Au lieu d'enchaîner manuellement les recherches, veuillez représenter le chemin sous forme de liste de clés. Veuillez ensuite utiliserreduce(operator.getitem, path, data)pour le parcourir. Cela permet de conserver un code générique, lisible et réutilisable.Pipelines de traitement des données
Reduce peut piloter des pipelines de traitement de données en faisant passer les données par une séquence de transformations. Chaque fonction traite une seule étape, et le pipeline résulte de leur application dans l'ordre. Voici un pipeline de démonstration qui prétraite une chaîne de texte avant de l'introduire dans un modèle NLP.
Gestion des erreurs dans les applications complexes
Revenons à l'exemple JSON imbriqué. À l'heure actuelle, l'appel direct à
reduce(operator.getitem, …)renvoie une exceptionKeyErrorouTypeErrorsi une clé est manquante ou s'il rencontre un élément qui n'est pas un dictionnaire. Pour renforcer la sécurité du code, veuillez définir une fonction d'aide qui encapsuleoperator.getitemdans un bloc try/except et renvoie une valeur par défaut en cas d'erreur.Voici une possibilité pour la fonction d'aide.
Maintenant, modifiez les exemples de recherche pour utiliser notre nouvelle fonction au lieu d'une fonction déballée.
reduce()Transformations de données en plusieurs étapes avec map() et filter()
Vous pouvez associer
reduce()à d'autres outils fonctionnels, tels quemap()etfilter(), afin de créer des transformations de données en plusieurs étapes. Voici notre pipeline de prétraitement NLP précédent, rédigé de manière fonctionnelle.Les transformations sont les suivantes :
« Le renard brun rapide saute par-dessus une bûche » → « le renard brun rapide saute par-dessus une bûche »
« rapide renard brun sautant sur une bûche » → « rapide renard brun saute sur une bûche »
Pour approfondir vos connaissances en matière de programmation fonctionnelle et de vectorisation, nous vous recommandons ces articles DataCamp.
Intégration avec l'écosystème Python moderne
Pour utiliser efficacement l'
reduce(), il est utile de comprendre comment il s'intègre dans l'écosystème Python moderne. Explorons comment il s'intègre à NumPy et pandas, comment il soutient les systèmes parallèles et distribués, et comment il interagit avec les outils modernes tels que les analyseurs statiques.numpy et pandas
NumPy et pandas fonctionnent à partir d'un code C optimisé, il est donc déconseillé de reproduire leurs fonctionnalités avec
reduce(). Cependant,reduce()constitue un choix judicieux pour les pipelines comportant des étapes dynamiques. Par exemple, vous pourriez composer de nombreuses transformations NumPy sur un tableau.Cadres informatiques parallèles et distribués
La réduction est un élément central des cadres informatiques parallèles et distribués. Ces systèmes fonctionnent en divisant un ensemble de données en partitions, en traitant ces partitions en parallèle, puis en combinant ces résultats partiels en une seule réponse. L'étape de « combinaison » correspond à la réduction.
Pour que la réduction fonctionne correctement dans un cluster, veuillez vous assurer que les conditions suivantes sont remplies.
Si ces propriétés ne sont pas respectées, les réductions deviennent lentes (car elles ne peuvent pas être parallélisées en toute sécurité) ou incorrectes (car les résultats dépendent de l'ordre d'évaluation).
Ce processus nécessite une opération associative et une identité claire. Dans le cas contraire, vous obtiendrez un code lent ou des résultats incorrects.
Outils d'analyse statique
Un analyseur statique (tel que mypy, Pyright, ruff ou bandit) est un outil qui inspecte le code sans l'exécuter. Ces outils détectent les erreurs, appliquent les règles de style et vérifient l'exactitude des types.
Les analyseurs statiques rencontrent des difficultés avec l'
reduce(). Dans le processus de pliage, le type d'accumulateur peut différer du type d'élément, et l'inférence de type devient confuse.Veuillez examiner ce code.
Même si ce code fonctionne correctement, un analyseur statique pourrait signaler quelques problèmes.
Afin de rendre le code plus clair pour les humains et les analyseurs, veuillez ajouter des indications de type.
Le réducteur affiche désormais explicitement
List[str]).str.list[str]».Grâce à ces indications, les analyseurs statiques peuvent vérifier le code de manière rigoureuse.
Conclusion
La fonction « reduce » provient de la programmation fonctionnelle, où la consolidation de collections en un seul résultat constitue un concept central. Même s'il ne s'agit plus d'un outil de premier ordre en Python, il a toujours sa place. Lorsque vous avez besoin de pipelines flexibles, de pliages personnalisés ou d'opérations qui ne correspondent pas parfaitement aux fonctions existantes,
reduce()est un outil puissant. Utilisé avec précaution, il s'intègre parfaitement à l'écosystème Python au sens large et reste un outil pratique dans les situations appropriées.Voici quelques liens Python connexes qui pourraient vous être utiles.