Ga naar hoofdinhoud

Python reduce(): Een complete gids

Leer wanneer en hoe je Python's reduce() gebruikt. Inclusief praktische voorbeelden en best practices.
Bijgewerkt 2 okt 2026  · 11 min lezen

Verken met AI

ChatGPTClaudePerplexity

Python's reduce()-functie komt uit de wereld van functioneel programmeren. Functioneel programmeren (FP) is een programmeerparadigma waarin programma's resultaten opbouwen door functies toe te passen op onveranderlijke data. 

Een veelvoorkomend patroon in deze stijl is de "fold", die een reeks samenklapt tot één resultaat. Bijvoorbeeld, het samenklappen van de lijst met getallen [2, 4, 5, 3] onder optelling levert 14 op via opeenvolgende stappen: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.

reduce() generaliseert dit idee. Het past een binaire operatie toe over een iterable totdat alleen het resultaat overblijft.

In dit artikel bespreek ik de kernelementen van Python's reduce() en geef ik enkele praktische voorbeelden. Heb je een opfrisser nodig van de basis van Python, bekijk dan deze bronnen:

Geschiedenis

Python bevat ook andere functies voor functioneel programmeren, zoals map() en filter(). 

Functionele constructies zoals map() en filter() zaten al in Python 1.0. Guido van Rossum was er geen fan van; hij wees erop dat reduce() lastig te doorgronden was en dat een for-lus bijna altijd beter leesbaar is. In Python 3.0, conform PEP 3100, verwijderden ontwikkelaars reduce() als ingebouwde functie en verplaatsten het naar de module functools. Deze verhuizing naar functools degradeerde het feitelijk tot een nichetool.

Waarom reduce() gebruiken?

Meestal is een ingebouwde functie of een lus de betere keuze. Toch is reduce() in sommige gevallen een goede optie.

  • Functionele pijplijnen. Koppel een (eventueel dynamische) reeks transformaties op een nette manier aan elkaar.
  • Algebraïsche folds. Gebruik voor operaties met natuurlijke identiteitswaarden, zoals vereniging van verzamelingen met de lege verzameling of bitmask-bewerkingen met nul.
  • Aangepaste fold zonder ingebouwde functie. Definieer je eigen merge op domeinspecifieke wijze wanneer er geen ingebouwde bestaat.
  • Gestructureerde accumulators. Houd meerdere stukjes toestand tegelijk bij binnen één aangepaste accumulatorfunctie.

Hoe Python reduce() werkt

Laten we de werking van reduce() verkennen.

De basisfunctiehandtekening van reduce() is 

functools.reduce(function, iterable, [initializer])

De reduce()-functie neemt twee verplichte argumenten en een optioneel derde argument.

  • function: een binaire functie die specificeert hoe twee elementen gecombineerd worden,
  • iterable: de sequentie of iterable om te reduceren, zoals een lijst of tuple, initializer (optioneel): een startwaarde om de functie mee te zaaien.

Stap-voor-stap voorbeeld:

  •  [1, 3, 2, 7] → [4, 2, 7].
  • [4, 2, 7] → [6, 7].
  • [6, 7] → [13].

Het eindresultaat is 13.

Eenvoudige reduce()-voorbeelden

De volgende speelgoedvoorbeelden tonen de werking van reduce(). 

from functools import reduce

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"

Initializer

Zonder initializer neemt reduce() het eerste element van de iterable als startwaarde. Als de iterable leeg is, geeft reduce() een TypeError. Om code robuust te maken, geef je een initializer op om het gedrag bij lege invoer te definiëren.

from functools import reduce

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, "")

Initializers zaaien ook een resultaat met een lege container. Zo kun je bijvoorbeeld woorden samenvoegen tot een platte lijst met karakters. 

from functools import reduce

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']

Wil je verder aan de slag met Python en datamanipulatie, dan raad ik deze opties aan.

De reducer definiëren

Tot nu toe hebben we lambda-functies gebruikt om de binaire operator te definiëren. 

Je kunt ook operatoren uit de module operator gebruiken.  De  operator-module bevat functievarianten van gangbare operatoren en method calls. In plaats van x + y gebruik je bijvoorbeeld operator.add(x, y). Zo kun je vooraf gedefinieerde (en efficiënte) operatoren doorgeven aan reduce() zonder een lambda te hoeven schrijven.

from functools import reduce
import operator as op

numbers = [2, 4, 6]
total = reduce(op.add, numbers, 0)  # instead of reduce(lambda x,y: x + y, numbers)

Een derde optie is een eigen functie schrijven. Dit is handig wanneer er geen vooraf gedefinieerde operator is of wanneer de functie te ingewikkeld is voor een lambda. 

Stel bijvoorbeeld dat je duplicaten uit een lijst wilt verwijderen, maar de volgorde van eerste voorkomen wilt behouden. Je zou een reducer kunnen definiëren die een item alleen aan een lijst toevoegt als het nog niet eerder is voorgekomen.  

from functools import reduce


items = ['the', 'wild', 'wild', 'world', 'is', 'the', 'wide', 'world', 'is', 'the', 'world']


def dedup(acc, x):
    if x not in acc:  # O(n) membership test
        acc.append(x)
    return acc




unique = reduce(dedup, items, [])  # ['the', 'wild', 'world', 'is', 'wide']

Voor meer ideeën over het verwijderen van duplicaten, bekijk deze tutorial.

Performance van Python reduce()

reduce() brengt performance-overhead met zich mee.

Overhead van functiekoppelingen

Python reduce() roept onze functie één keer per element aan. Bij een lijst met een miljoen items betekent dit een miljoen functieaanroepen, die elk een frame creëren, argumenten afhandelen en referentietellingen bijwerken. Dit zorgt voor aanzienlijke overhead.

Ter vergelijking: een ingebouwde functie zoals sum doet één enkele aanroep naar een C-functie en voert de miljoen optellingen uit binnen de C-lus. Dit verschil kan de ingebouwde functie orders van grootte sneller maken.

Cache-locality en CPU-efficiëntie

Reduce lijdt ook onder slechte cache-locality en CPU-efficiëntie.  

Een moderne CPU kan enkele miljarden instructies per seconde uitvoeren, maar RAM-toegang is vele ordes van grootte trager. Ter compensatie hebben moderne CPU's caches (L1, L2, L3) die data opslaan voor snelle toegang.

Deze caches benutten twee patronen. 

  • Tijdelijke locality: data die recent gebruikt is, zal waarschijnlijk opnieuw gebruikt worden. 
  • Ruimtelijke locality: data nabij een recent gebruikt adres zal waarschijnlijk binnenkort gebruikt worden.

Daarentegen omvat elke stap van reduce() pointer-chasing om het volgende element te vinden en Python-functieaanroepen, wat de locality doorbreekt en de CPU doet stagneren. Ingebouwde en gevectoriseerde functies vermijden dit probleem door in strakke C-lussen te draaien.

Voor een opfrisser over het schrijven van idiomatische en efficiënte Python-code, bekijk:

Alternatieven voor Python reduce()

Ingebouwde functies:

  • Geoptimaliseerde C-code. Ingebouwde functies draaien hun lussen in geoptimaliseerde C-code, niet in Python. Dat voorkomt de overhead die reduce() met zich meebrengt. Dit snelheidsvoordeel stapelt op bij grote invoer. 
  • Leesbaarheid. Ingebouwde functies hebben beschrijvende namen (sum, min), dus hun bedoeling is duidelijk. Een aanroep van reduce() dwingt je om de gevouwen functie te ontcijferen.

Lussen:

  • Performance. Lussen draaien over het algemeen trager dan ingebouwde functies, maar sneller dan reduce().
  • Leesbaarheid. Net als ingebouwde functies zijn lussen meestal beter leesbaar dan reduce(). Een aanroep van reduce() dwingt je tot het parsen van een functionele uitspraak, terwijl een lus meer Pythonic is.

itertools.accumulate()

Python's itertools-bibliotheek biedt een verzameling performante iterators zoals count(), product() en combinations(). Een nuttige itertools-functie is itertools.accumulate(). Net als reduce() vouwt het een functie over een iterable. accumulate() bewaart echter de tussenresultaten van de berekening, niet alleen het eindresultaat. 

Bijvoorbeeld,

import itertools, operator
from functools import reduce

list(itertools.accumulate([1, 2, 3, 4], initial=0)) # [0, 1, 3, 6, 10]
reduce(operator.add, [1, 2, 3, 4], 0) # 10

Accumulate is handig wanneer je lopende totalen of minima/maxima nodig hebt. Je wilt bijvoorbeeld maand na maand de maximale temperatuur weten.

Veelvoorkomende valkuilen bij het gebruik van reduce()

Wanneer je reduce() gebruikt, houd dan de volgende valkuilen in gedachten.

  • Geef de voorkeur aan eenvoudigere alternatieven zoals ingebouwde functies of lussen. Bewaar reduce() voor wanneer je het echt nodig hebt.
  • Ga om met lege iterables. Gebruik altijd een geschikte initializer om een fout bij lege invoer te vermijden.
  • Let op geheugenproblemen. Dwing reduce() niet in situaties waar een generator- of streamingaanpak efficiënter zou zijn.
  • Vermijd lastige lambda's. Gebruik functies uit de module operator wanneer dat kan. Lambda's, zeker bij niet-associatieve operaties, kunnen de helderheid schaden.
  • Kies duidelijkheid boven slimheid.

Best practices en richtlijnen voor Python reduce() 

Zoals met elke tool zijn er best practices voor reduce(). Als je hebt besloten dat reduce() de juiste tool is, houd dan deze richtlijnen aan.

Ontwerp eerst de reducer 

  • Definieer het contract in één zin. "Combineer dictionary-keys door tellingen per key op te tellen."
  • Houd het indien mogelijk associatief. Dit maakt paralleliseren en testen eenvoudiger.
  • Identificeer je identiteitselement en gebruik dit als initializer. Voor som is de identiteit bijvoorbeeld 0. Voor min is dat math.inf, en voor set union is dat set(). Zo blijft de code robuust en vrij van TypeErrors.

Houd het simpel en leesbaar

  • Eén duidelijke bewerking. Geen neveneffecten.
  • Geef de reducer een beschrijvende naam.

Documenteer

  • Leg in de docstring vast: identiteits­element en gedrag bij lege invoer, associativiteits­aanname, foutafhandeling.

Test

  • Unittests op randgevallen: lege iterable, gemengde types, extreme waarden.
  • Test associativiteit.

Monitor performance

  • Benchmark kleine en grote invoer. Vergelijk met ingebouwde functies en lussen.
  • Als snelheid telt, overweeg pre-processing, batchen en zware berekeningen verplaatsen naar NumPy of pandas.

Geavanceerde toepassingen en realistische use-cases voor Python reduce()

Gezien de nadelen lijkt het misschien dat reduce() geen echte waarde heeft. Integendeel, reduce() heeft veel nuttige use-cases. 

  • Verwerken van geneste structuren
  • Database-achtige operaties
  • Dataverwerkingspijplijnen
  • MapReduce-toepassingen

Verwerken van geneste structuren

Reduce biedt een nette manier om door geneste datastructuren, zoals JSON-objecten, te traverseren door een reeks keys te vouwen tot opeenvolgende lookups.

import json
from functools import reduce
import operator

data = json.loads('''
{
  "user": {
    "id": "ABC123",
    "name": "Alice",
    "email": "alice@example.com",
    "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)  # 34

Hier is reduce() logisch. De JSON is diep genest: user → profile → address → city. In plaats van lookups handmatig te koppelen, representeer je het pad als een lijst met keys. Gebruik vervolgens reduce(operator.getitem, path, data) om het te traverseren. Dit houdt de code generiek, leesbaar en herbruikbaar.

Dataverwerkingspijplijnen

Reduce kan dataverwerkingspijplijnen aansturen door data door een reeks transformaties te leiden. Elke functie handelt een enkele stap af en de pijplijn ontstaat door ze op volgorde toe te passen. Hier is een eenvoudig voorbeeld dat een tekststring voorbewerkt voordat die aan een NLP-model wordt gevoerd. 

from functools import reduce
import re

# Define preprocessing steps
def strip_punctuation(s):
    return re.sub(r"[^\w\s]", "", s)

def to_lower(s):
    return s.lower()

def remove_stopwords(s):
    stops = {"the", "is", "a", "of"}
    return " ".join(word for word in s.split() if word not in stops)

def stem_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 log

Foutafhandeling in complexe toepassingen

Laten we teruggaan naar het geneste JSON-voorbeeld. Nu gooit de directe aanroep van reduce(operator.getitem, …) een KeyError of TypeError als een key ontbreekt of als er een niet-dict wordt aangetroffen. Om de code veiliger te maken, definieer je een helperfunctie die operator.getitem omhult met een try/except-blok en een standaardwaarde teruggeeft wanneer er een fout optreedt.

Hier is een mogelijke helperfunctie.

def deep_get(data, keys, default=None):
    """Traverse nested dicts/lists safely with reduce."""
    try:
        return reduce(operator.getitem, keys, data)
    except (KeyError, IndexError, TypeError):
        return default

Pas nu de voorbeeld-lookups aan zodat ze onze nieuwe functie gebruiken in plaats van een onomhulde 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"

Meerstapsdatatransformaties met map() en filter()

Je kunt reduce() combineren met andere functionele tools, zoals map() en filter(), om meerstapsdatatransformaties te bouwen. Hier is onze eerdere NLP-voorbewerking functioneel geschreven.

from functools import reduce
import re

# Define preprocessing steps
def strip_punctuation(s):
    return re.sub(r"[^\w\s]", "", s)

def to_lower(s):
    return " ".join(map(str.lower, s.split()))

def remove_stopwords(s):
    stops = {"the", "is", "a", "of"}
    return " ".join(filter(lambda w: w not in stops, s.split()))

def stem_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 log

De transformaties zijn:

  • Verwijder leestekens. "The quick brown fox is Jumping over a log." → "The quick brown fox is Jumping over a log"
  • Kleine letters

"The quick brown fox is Jumping over a log" → "the quick brown fox is jumping over a log"

  • Verwijder stopwoorden. "the quick brown fox is jumping over a log" → "quick brown fox jumping over log"
  • Stem woorden

 "quick brown fox jumping log"  → "quick brown fox jump log"

Wil je functioneel programmeren en vectorisatie verder verkennen, bekijk dan deze DataCamp-artikelen.

Integratie met het moderne Python-ecosysteem

Om reduce() goed te gebruiken helpt het om te begrijpen hoe het past in het moderne Python-ecosysteem. Laten we bekijken hoe het zich verhoudt tot NumPy en pandas, hoe het ten grondslag ligt aan parallelle en gedistribueerde systemen, en hoe het samenwerkt met moderne tooling zoals statische analyzers. 

numpy en pandas

NumPy en pandas draaien op geoptimaliseerde C-code, dus dupliceer hun functionaliteit niet met reduce(). Wel is reduce() een goede keuze voor pijplijnen met dynamische stappen. Je kunt bijvoorbeeld veel NumPy-transformaties op een array componeren.

from functools import reduce
import numpy as np

def standardize(x):
    return (x - x.mean()) / (x.std() + 1e-9)

def clip(x):
    return np.clip(x, 0, 1)

def log(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 ndarray

Parallelle en gedistribueerde computing-frameworks

Reduce is centraal in parallelle en gedistribueerde computing-frameworks. Deze systemen werken door een dataset op te splitsen in partities, die partities parallel te verwerken en vervolgens de partiële resultaten te combineren tot één antwoord. De "combine"-stap is de reductie.

Opdat reductie correct werkt over een cluster, moeten de volgende voorwaarden gelden.

  • Associativiteit. De operatie moet hetzelfde resultaat geven ongeacht de groepering. Dit maakt het mogelijk partiële resultaten in elke volgorde samen te voegen over het netwerk.
  • Identiteitselement. De operatie moet een initializer hebben die het eindresultaat niet beïnvloedt. Dit waarborgt correctheid wanneer partities leeg zijn of ongelijk van grootte. 

Als deze eigenschappen niet gelden, worden reducties traag (omdat ze niet veilig te paralleliseren zijn) of onjuist (omdat resultaten afhangen van de evaluatievolgorde).

Dit proces heeft dus een associatieve operatie en een duidelijk identiteitselement nodig. Anders krijg je trage code of onjuiste resultaten.

Statische analysetools

Een statische analyzer (zoals mypy, Pyright, ruff of bandit) is een tool die code inspecteert zonder deze uit te voeren. Deze tools vangen bugs, handhaven stijlafspraken en controleren typecorrectheid.

Statische analyzers hebben moeite met reduce(). In het vouwproces kan het accumulatortype verschillen van het elementtype, en type-inferentie wordt rommelig. 

Beschouw deze code.

from functools import reduce

def add_chars(acc, word):
    acc.extend(word)   # extend adds each character of the string
    return acc

chars = reduce(add_chars, ["hi", "ok"], [])
print(chars)  # ['h', 'i', 'o', 'k']

Hoewel deze code prima draait, kan een statische analyzer over een paar dingen klagen.

  • Het elementtype van de initializer [] is ambigu. 
  • Analyzers gaan er mogelijk vanuit dat accumulator- en elementtypen overeenkomen.

Maak de code duidelijker voor mensen en analyzers door typehints toe te voegen.

from functools import reduce
from typing import List

def add_chars(acc: List[str], word: str) -> List[str]:
    acc.extend(word)   # extend adds each character of the string
    return acc

chars: List[str] = reduce(add_chars, ["hi", "ok"], [])
print(chars)  # ['h', 'i', 'o', 'k']

Nu toont de reducer expliciet

  • De accumulator is een List[str].
  • Elk element is een str.
  • Het geretourneerde type is een list[str].

Met deze hints kunnen statische analyzers de code rigoureus controleren. 

Conclusie

Reduce komt uit het functioneel programmeren, waar het vouwen van collecties tot één resultaat een kernidee is. Hoewel het geen eersteklas hulpmiddel meer is in Python, heeft het zijn plaats. Wanneer je flexibele pijplijnen, aangepaste folds of operaties nodig hebt die niet netjes op bestaande functies passen, is reduce() een krachtig hulpmiddel. Zorgvuldig gebruikt integreert het netjes met het bredere Python-ecosysteem en blijft het een praktische tool in de juiste situaties.

Enkele gerelateerde Python-links die je wellicht handig vindt.

Python reduce() FAQs

Wat doet reduce()?

Het past herhaaldelijk een twee-argumentenfunctie toe op een iterable om die te reduceren tot één resultaat.

Waar is reduce() gedefinieerd?

Voor versie 3 was het een ingebouwde functie. Sindsdien staat het in de module functools.

Wat voor soort functies moet ik aan reduce() doorgeven?

Eenvoudige, associatieve en goed gedocumenteerde functies. Vermijd neveneffecten en niet-associatieve logica.

Hoe verhoudt het zich tot itertools.accumulate()?

reduce() geeft alleen het eindresultaat terug, accumulate() levert alle tussenresultaten op.

Wanneer moet ik een initializer gebruiken?

Gebruik een initializer wanneer de iterable leeg kan zijn.

Onderwerpen
Python
Data Science

Topcursussen in Python

Cursus

Efficiënte Python-code schrijven

4 uur
156.2K
Leer efficiënte code schrijven die snel wordt uitgevoerd en middelen vakkundig toewijst om onnodige overhead te voorkomen.
Bekijk detailsRight Arrow
Start Cursus
Meer bekijkenRight Arrow
Gerelateerd

blog

AI vanaf nul leren in 2026: een complete gids van de experts

Ontdek alles wat je moet weten om in 2026 AI te leren, van tips om te beginnen tot handige resources en inzichten van industrie-experts.
Adel Nehme's photo

Adel Nehme

15 min

Meer BekijkenMeer Bekijken